/**
* 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];
}
}