com.aptana.ide.editors.unified.UnifiedReconcilingStrategy.java Source code

Java tutorial

Introduction

Here is the source code for com.aptana.ide.editors.unified.UnifiedReconcilingStrategy.java

Source

/**
 * This file Copyright (c) 2005-2008 Aptana, Inc. This program is
 * dual-licensed under both the Aptana Public License and the GNU General
 * Public license. You may elect to use one or the other of these licenses.
 * 
 * This program is distributed in the hope that it will be useful, but
 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
 * NONINFRINGEMENT. Redistribution, except as permitted by whichever of
 * the GPL or APL you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or modify this
 * program under the terms of the GNU General Public License,
 * Version 3, as published by the Free Software Foundation.  You should
 * have received a copy of the GNU General Public License, Version 3 along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 * Aptana provides a special exception to allow redistribution of this file
 * with certain other free and open source software ("FOSS") code and certain additional terms
 * pursuant to Section 7 of the GPL. You may view the exception and these
 * terms on the web at http://www.aptana.com/legal/gpl/.
 * 
 * 2. For the Aptana Public License (APL), this program and the
 * accompanying materials are made available under the terms of the APL
 * v1.0 which accompanies this distribution, and is available at
 * http://www.aptana.com/legal/apl/.
 * 
 * You may view the GPL, Aptana's exception and additional terms, and the
 * APL in the file titled license.html at the root of the corresponding
 * plugin containing this source file.
 * 
 * Any modifications to this file must keep this entire header intact.
 */
package com.aptana.ide.editors.unified;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.reconciler.DirtyRegion;
import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector;
import org.eclipse.ui.texteditor.spelling.SpellingContext;
import org.eclipse.ui.texteditor.spelling.SpellingProblem;
import org.eclipse.ui.texteditor.spelling.SpellingService;

import com.aptana.commons.spelling.engine.IComputeSpellcheckRegions;
import com.aptana.commons.spelling.engine.SpellingAnnotation;
import com.aptana.ide.editors.UnifiedEditorsPlugin;
import com.aptana.ide.editors.unified.colorizer.LanguageColorizer;
import com.aptana.ide.editors.unified.folding.FoldingExtensionPointLoader;
import com.aptana.ide.editors.unified.folding.LanguageProjectAnnotation;
import com.aptana.ide.editors.unified.folding.FoldingExtensionPointLoader.FoldingStructure;
import com.aptana.ide.lexer.LexemeList;
import com.aptana.ide.parsing.IParseState;
import com.aptana.ide.parsing.nodes.IParseNode;

/**
 * UnifiedReconcilingStrategy
 */
public class UnifiedReconcilingStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension {
    /*
     * Fields
     */
    private UnifiedEditor _editor;
    private IDocument _document;
    private boolean _isDisposing = false;
    private boolean firstReconcile = true;
    private Color foldingFgColor;

    /** Holds the language annotations mapped to their positions */
    protected final Map annotations = new HashMap();

    /** Holds language mapped to folding structure objects */
    protected Map childTypes = new HashMap();

    protected SpellingAnnotation[] spellingAnnotations;
    private SpellingService spellingService;
    private IPreferenceStore editorsPreferenceStore;
    private IPropertyChangeListener editorsListener;

    /**
     * dispose
     */
    public void dispose() {
        this._isDisposing = true;
        this._editor = null;
        this._document = null;
        editorsPreferenceStore.removePropertyChangeListener(editorsListener);
    }

    /**
     * @return Returns the editor.
     */
    public IUnifiedEditor getEditor() {
        return this._editor;
    }

    /**
     * setEditor
     * 
     * @param editor
     */
    public void setEditor(UnifiedEditor editor) {
        this._editor = editor;
        childTypes = FoldingExtensionPointLoader.loadChildTypes(editor);

        editorsPreferenceStore = EditorsUI.getPreferenceStore();
        spellingService = new SpellingService(editorsPreferenceStore);
        editorsListener = new IPropertyChangeListener() {

            public void propertyChange(PropertyChangeEvent event) {
                doSpellCheck(spellingService, _editor.getViewer());
            }

        };
        editorsPreferenceStore.addPropertyChangeListener(editorsListener);
    }

    /**
     * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#setDocument(org.eclipse.jface.text.IDocument)
     */
    public void setDocument(IDocument document) {
        this._document = document;

    }

    /**
     * getDocument
     * 
     * @return IDocument
     */
    public IDocument getDocument() {
        return this._document;
    }

    /**
     * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.reconciler.DirtyRegion,
     *      org.eclipse.jface.text.IRegion)
     */
    public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) {
        calculatePositions();
    }

    /**
     * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.IRegion)
     */
    public void reconcile(IRegion partition) {
        calculatePositions();
    }

    /**
     * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#setProgressMonitor(org.eclipse.core.runtime.IProgressMonitor)
     */
    public void setProgressMonitor(IProgressMonitor monitor) {
        // TODO Auto-generated method stub
    }

    /**
     * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#initialReconcile()
     */
    public void initialReconcile() {
        if (this._isDisposing) {
            return;
        }

        calculatePositions();
        firstReconcile = false;
    }

    /**
     * calculatePositions
     */
    protected void calculatePositions() {
        if (this._isDisposing) {
            return;
        }

        annotations.clear();
        LexemeList ll = this._editor.getFileContext().getLexemeList();

        if (ll != null) {
            IParseState parseState = this._editor.getFileContext().getParseState();

            parseForRegions(parseState);
        }

        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                if (!_isDisposing) {
                    _editor.updateFoldingStructure(annotations);
                }
            }

        });
        if (_editor != null)
            doSpellCheck(spellingService, (SourceViewer) _editor.getViewer());
    }

    /**
     * emitPosition
     * 
     * @param startOffset
     * @param length
     * @param type
     * @param language
     */
    public void emitPosition(int startOffset, int length, String type, String language) {
        if (this._isDisposing) {
            return;
        }

        try {
            IDocument doc = this._document;

            if (doc != null) {
                int startLine = doc.getLineOfOffset(startOffset);
                int endLine = doc.getLineOfOffset(startOffset + length);

                if (startLine < endLine) {
                    int start = doc.getLineOffset(startLine);
                    int end = doc.getLineOffset(endLine) + doc.getLineLength(endLine);
                    Position position = new Position(start, end - start);
                    LanguageProjectAnnotation annotation = new LanguageProjectAnnotation(language, type);

                    LanguageColorizer colorizer = LanguageRegistry.getLanguageColorizer(language);
                    Color fgColor = null;
                    Color bgColor = null;
                    if (colorizer != null && colorizer.getFoldingFg() != null) {
                        fgColor = colorizer.getFoldingFg();
                        bgColor = colorizer.getFoldingBg();
                        annotation.setColor(colorizer.getFoldingFg());
                    } else if (foldingFgColor != null) {
                        fgColor = foldingFgColor;
                        annotation.setColor(foldingFgColor);
                    }
                    if (fgColor != null) {
                        annotation.setCollapsedImage(annotation.getCollapsedImage(), fgColor, bgColor);
                        annotation.setExpandedImage(annotation.getExpandedImage(), fgColor, bgColor);
                    }

                    String pref = FoldingExtensionPointLoader.createInitialFoldingPreferenceId(language, type);
                    IPreferenceStore store = UnifiedEditorsPlugin.getDefault().getPreferenceStore();

                    if (firstReconcile) {
                        if (store.getBoolean(pref)) {
                            annotation.markCollapsed();
                        }
                    }
                    annotations.put(annotation, position);
                }
            }
        } catch (BadLocationException e) {
            // Do not log error here, if a bad location occurs the document must
            // be changing rapidly and this will
            // eventually right itself and correct folding will be restored
        }
    }

    /**
     * parseForRegions
     * 
     * @param parseState
     */
    public void parseForRegions(IParseState parseState) {
        IParseNode node = parseState.getParseResults();

        if (node != null) {
            emitChildren(node.getChildren());
            emitChildren(parseState.getCommentRegions());
        }
    }

    private void emitChildren(IParseNode[] nodes) {
        IPreferenceStore store = UnifiedEditorsPlugin.getDefault().getPreferenceStore();
        for (int i = 0; i < nodes.length; i++) {
            IParseNode node = nodes[i];
            if (node.getStartingOffset() >= 0) {
                if (childTypes.containsKey(node.getLanguage())) {
                    FoldingStructure fs = (FoldingStructure) childTypes.get(node.getLanguage());
                    if (node.getChildCount() > 0) {
                        if (fs.getHandler() == null || fs.getHandler().nodeIsFoldable(node)) {
                            if (fs.foldAllParents() || fs.getTypes().containsKey(node.getName())) {
                                String prefID = FoldingExtensionPointLoader
                                        .createEnablePreferenceId(fs.getLanguage());
                                if (store.getBoolean(prefID)) {
                                    this.emitPosition(node.getStartingOffset(),
                                            node.getEndingOffset() - node.getStartingOffset(), node.getName(),
                                            node.getLanguage());
                                }
                            }
                        }
                        emitChildren(node.getChildren());
                    } else {
                        String nm = (node instanceof IHasCustomFoldingType)
                                ? ((IHasCustomFoldingType) node).getFoldingTypeName()
                                : node.getName();
                        if (fs.getTypes().containsKey(nm)
                                || (fs.getHandler() != null && fs.getHandler().nodeIsFoldable(node))) {
                            String prefID = FoldingExtensionPointLoader.createEnablePreferenceId(fs.getLanguage());
                            if (store.getBoolean(prefID)) {
                                this.emitPosition(node.getStartingOffset(),
                                        node.getEndingOffset() - node.getStartingOffset(), node.getName(),
                                        node.getLanguage());
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Sets the folding annotation hover color
     * 
     * @param color
     */
    public void setFoldingAnnotationHoverColor(Color color) {
        this.foldingFgColor = color;
    }

    private void doSpellCheck(final SpellingService spellingService, final ISourceViewer sv) {
        final ISpellingProblemCollector collector = new ISpellingProblemCollector() {

            ArrayList<SpellingProblem> prblms = new ArrayList<SpellingProblem>();

            public void accept(SpellingProblem problem) {
                this.prblms.add(problem);
            }

            public void beginCollecting() {
                this.prblms.clear();
            }

            public void endCollecting() {
                final AnnotationModel annotationModel = (AnnotationModel) sv.getAnnotationModel();
                if (annotationModel != null) {
                    final Map<SpellingAnnotation, Position> annotationsToAdd = new HashMap<SpellingAnnotation, Position>();
                    for (final SpellingProblem p : this.prblms) {
                        final SpellingAnnotation annotation = new SpellingAnnotation(p);
                        annotationsToAdd.put(annotation, new Position(p.getOffset(), p.getLength()));
                    }

                    annotationModel.replaceAnnotations(
                            spellingAnnotations == null ? new Annotation[] {} : spellingAnnotations,
                            annotationsToAdd);
                    spellingAnnotations = new SpellingAnnotation[annotationsToAdd.size()];
                    annotationsToAdd.keySet().toArray(spellingAnnotations);
                }
            }
        };
        final SpellingContext context = new SpellingContext();
        spellingService.check(sv.getDocument(), computeSpellCheckRegions(), context, collector, null);
    }

    private IRegion[] computeSpellCheckRegions() {
        IComputeSpellcheckRegions adapter = (IComputeSpellcheckRegions) this._editor
                .getAdapter(IComputeSpellcheckRegions.class);
        if (adapter != null) {
            return adapter.spellCheckRegions();
        }
        LexemeList ll = this._editor.getFileContext().getLexemeList();

        if (ll != null) {
            IParseState parseState = this._editor.getFileContext().getParseState();

            IParseNode[] commentRegions = parseState.getCommentRegions();
            IRegion[] regions = new IRegion[commentRegions.length];
            for (int a = 0; a < commentRegions.length; a++) {
                regions[a] = new Region(commentRegions[a].getStartingOffset(), commentRegions[a].getLength());
            }
            return regions;
        }

        return new IRegion[0];
    }
}