/* Reconciler for Ptolemy semantic highlighting. Copyright (c) 2005-2013 The Regents of the University of California. All rights reserved. Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. PT_COPYRIGHT_VERSION_2 COPYRIGHTENDKEY */ package ptolemy.backtrack.eclipse.plugin.editor; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.internal.corext.dom.GenericVisitor; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider; import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility; import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; import org.eclipse.jdt.internal.ui.javaeditor.JavaSourceViewer; import org.eclipse.jdt.internal.ui.text.JavaPresentationReconciler; import org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener; import org.eclipse.jdt.ui.text.IColorManager; import org.eclipse.jdt.ui.text.IColorManagerExtension; import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferenceConverter; import org.eclipse.jface.resource.StringConverter; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextInputListener; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.TextAttribute; import org.eclipse.jface.text.TextPresentation; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbenchPartSite; import ptolemy.backtrack.eclipse.plugin.EclipsePlugin; /////////////////////////////////////////////////////////////////// //// SemanticHighlightingReconciler /** Reconciler for Ptolemy semantic highlighting.
This is the main class for Ptolemy semantic highlighting. It parses the Java
source in the editor on-the-fly, and add Ptolemy semantic coloring to the
presentation.
@author Thomas Feng
@version $Id: SemanticHighlightingReconciler.java 65763 2013-03-07 01:54:37Z cxh $
@since Ptolemy II 5.1
@Pt.ProposedRating Red (tfeng)
@Pt.AcceptedRating Red (tfeng)
*/
public class SemanticHighlightingReconciler implements
IJavaReconcilingListener, IPropertyChangeListener, ITextInputListener {
///////////////////////////////////////////////////////////////////
//// public methods ////
/** Prepare to reconcile the document. Nothing needs to be done in this
* method.
*/
public void aboutToBeReconciled() {
}
/** Prepare for the change of the input document. This method is invoked
* before the document is changed.
*
* @param oldInput The old input document.
* @param newInput The new input document.
*/
public void inputDocumentAboutToBeChanged(IDocument oldInput,
IDocument newInput) {
synchronized (_jobLock) {
_cancelJobs = true;
if (_job != null) {
_job.cancel();
}
}
}
/** Handle the change of input document. This method is invoked after the
* document is changed.
*
* @param oldInput The old input document.
* @param newInput The new input document.
*/
public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
synchronized (_jobLock) {
_cancelJobs = false;
}
if (newInput != null) {
_scheduleJob();
}
}
/** Install this reconciler to the editor, and enable it if it is set to be
* enabled.
*
* @param editor The editor.
* @param colorManager The color manager.
*/
public void install(PtolemyEditor editor, IColorManager colorManager) {
_editor = editor;
_colorManager = colorManager;
_preferenceStore = EclipsePlugin.getDefault().getPreferenceStore();
_preferenceStore.addPropertyChangeListener(this);
if (isEnabled()) {
_enable();
}
}
/** Return whether the semantic highlighting is enabled.
*
* @return true if the semantic highlighting is enabled.
*/
public boolean isEnabled() {
return SemanticHighlightings.isEnabled(_preferenceStore);
}
/** Handle the change of a property in the preference. This method is called
* if the user sets a preference in the preference dialog.
*
* @param event The property change event.
*/
public void propertyChange(PropertyChangeEvent event) {
_handlePropertyChangeEvent(event);
}
/** Perform operation after the reconcilation is finished.
*
* @param ast The compilation unit of the Java source.
* @param forced Whether the reconcilation is forced.
* @param progressMonitor The progress monitor.
*/
public void reconciled(CompilationUnit ast, boolean forced,
IProgressMonitor progressMonitor) {
_jobPresenter = _presenter;
_jobSemanticHighlightings = _semanticHighlightings;
_jobHighlightings = _highlightings;
if ((_jobPresenter == null) || (_jobSemanticHighlightings == null)
|| (_jobHighlightings == null)) {
_jobPresenter = null;
_jobSemanticHighlightings = null;
_jobHighlightings = null;
return;
}
_jobPresenter.setCanceled(progressMonitor.isCanceled());
if ((ast == null) || _jobPresenter.isCanceled()) {
return;
}
ASTNode[] subtrees = new ASTNode[] { ast };
_startReconcilingPositions();
if (!_jobPresenter.isCanceled()) {
_reconcilePositions(subtrees);
}
TextPresentation textPresentation = null;
if (!_jobPresenter.isCanceled()) {
textPresentation = _jobPresenter.createPresentation(
_addedPositions, _removedPositions);
}
if (!_jobPresenter.isCanceled()) {
_updatePresentation(textPresentation, _addedPositions,
_removedPositions);
}
_stopReconcilingPositions();
_jobPresenter = null;
_jobSemanticHighlightings = null;
_jobHighlightings = null;
}
///////////////////////////////////////////////////////////////////
//// protected inner classes ////
///////////////////////////////////////////////////////////////////
//// HighlightedPosition
/**
The position of a semantic highlighting.
@author Thomas Feng
@version $Id: SemanticHighlightingReconciler.java 65763 2013-03-07 01:54:37Z cxh $
@since Ptolemy II 5.1
@Pt.ProposedRating Red (tfeng)
@Pt.AcceptedRating Red (tfeng)
*/
protected static class HighlightedPosition extends Position {
/** Initialize the styled positions with the given offset, length and
* foreground color.
*
* @param offset The position offset.
* @param length The position length.
* @param highlighting The position's highlighting style.
* @param lock The lock object.
*/
public HighlightedPosition(int offset, int length,
HighlightingStyle highlighting, Object lock) {
super(offset, length);
_style = highlighting;
_lock = lock;
}
/** Return a corresponding style range.
*
* @return The style range.
*/
public StyleRange createStyleRange() {
if (_style.isEnabled()) {
return new StyleRange(getOffset(), getLength(), _style
.getTextAttribute().getForeground(), _style
.getTextAttribute().getBackground(), _style
.getTextAttribute().getStyle());
} else {
return new StyleRange(getOffset(), 0, _style.getTextAttribute()
.getForeground(), _style.getTextAttribute()
.getBackground(), _style.getTextAttribute().getStyle());
}
}
/** Mark this position as deleted.
*
* @see #undelete()
*/
public void delete() {
synchronized (_lock) {
super.delete();
}
}
/** Return the highlighting style.
*
* @return The highlighting style.
*/
public HighlightingStyle getHighlighting() {
return _style;
}
/** Test whether this position is contained in the given range.
*
* @param offset The range offset.
* @param length The range length.
* @return true if this position is not delete and contained in the
* given range.
*/
public boolean isContained(int offset, int length) {
synchronized (_lock) {
return !isDeleted() && (offset <= getOffset())
&& ((offset + length) >= (getOffset() + getLength()));
}
}
/** Test whether this highlighting's starting offset, length, and style
* is equal to the given parameters.
*
* @param offset The offset.
* @param length The length.
* @param highlighting The highlighting style.
* @return true if the given offset, length and highlighting are equal
* to the internal ones.
*/
public boolean isEqual(int offset, int length,
HighlightingStyle highlighting) {
synchronized (_lock) {
return !isDeleted() && (getOffset() == offset)
&& (getLength() == length) && (_style == highlighting);
}
}
/** Set the length of this position.
*
* @param length The new length.
*/
public void setLength(int length) {
synchronized (_lock) {
super.setLength(length);
}
}
/** Set the offset of this position.
*
* @param offset The new offset.
*/
public void setOffset(int offset) {
synchronized (_lock) {
super.setOffset(offset);
}
}
/** Mark this position as not deleted.
*
* @see #delete()
*/
public void undelete() {
synchronized (_lock) {
super.undelete();
}
}
/** Update this position with the new offset and new length.
*
* @param offset The new offset.
* @param length The new length.
*/
public void update(int offset, int length) {
synchronized (_lock) {
super.setOffset(offset);
super.setLength(length);
}
}
/** Lock object.
*/
private Object _lock;
/** Highlighting of the position.
*/
private HighlightingStyle _style;
}
///////////////////////////////////////////////////////////////////
//// HighlightingStyle
/**
The semantic highlighting style.
@author Thomas Feng
@version $Id: SemanticHighlightingReconciler.java 65763 2013-03-07 01:54:37Z cxh $
@since Ptolemy II 5.1
@Pt.ProposedRating Red (tfeng)
@Pt.AcceptedRating Red (tfeng)
*/
protected static class HighlightingStyle {
/** Construct a highlighting style with the given text attribute.
*
* @param textAttribute The text attribute.
* @param isEnabled Whether this style is enabled.
*/
public HighlightingStyle(TextAttribute textAttribute, boolean isEnabled) {
setTextAttribute(textAttribute);
setEnabled(isEnabled);
}
/** Return the text attribute.
*
* @return The text attribute.
* @see #setTextAttribute(TextAttribute)
*/
public TextAttribute getTextAttribute() {
return _textAttribute;
}
/** Return whether this style is enabled.
*
* @return true if this style is enabled.
*/
public boolean isEnabled() {
return _enabled;
}
/** Set whether this style is enabled.
*
* @param isEnabled true if this style is enabled.
*/
public void setEnabled(boolean isEnabled) {
_enabled = isEnabled;
}
/** Set the text attribute.
*
* @param textAttribute The text attribute.
* @see #getTextAttribute()
*/
public void setTextAttribute(TextAttribute textAttribute) {
_textAttribute = textAttribute;
}
/** Enabled state.
*/
private boolean _enabled;
/** Text attribute.
*/
private TextAttribute _textAttribute;
}
///////////////////////////////////////////////////////////////////
//// private methods ////
/** Adapt to a change on the enabled state.
*
* @param highlighting The highlighting style.
* @param event The event of the change.
*/
private void _adaptToEnablementChange(HighlightingStyle highlighting,
PropertyChangeEvent event) {
Object value = event.getNewValue();
boolean eventValue;
if (value instanceof Boolean) {
eventValue = ((Boolean) value).booleanValue();
} else if (IPreferenceStore.TRUE.equals(value)) {
eventValue = true;
} else {
eventValue = false;
}
highlighting.setEnabled(eventValue);
}
/** Adapt to a change on the text background.
*
* @param highlighting The highlighting style.
* @param event The event of the change.
*/
private void _adaptToTextForegroundChange(HighlightingStyle highlighting,
PropertyChangeEvent event) {
RGB rgb = null;
Object value = event.getNewValue();
if (value instanceof RGB) {
rgb = (RGB) value;
} else if (value instanceof String) {
rgb = StringConverter.asRGB((String) value);
}
if (rgb != null) {
String property = event.getProperty();
Color color = _colorManager.getColor(property);
if (((color == null) || !rgb.equals(color.getRGB()))
&& _colorManager instanceof IColorManagerExtension) {
IColorManagerExtension ext = (IColorManagerExtension) _colorManager;
ext.unbindColor(property);
ext.bindColor(property, rgb);
color = _colorManager.getColor(property);
}
TextAttribute oldAttr = highlighting.getTextAttribute();
highlighting.setTextAttribute(new TextAttribute(color, oldAttr
.getBackground(), oldAttr.getStyle()));
}
}
/** Adapt to a change on text style.
*
* @param highlighting The highlighting style.
* @param event The event of the change.
*/
private void _adaptToTextStyleChange(HighlightingStyle highlighting,
PropertyChangeEvent event, int styleAttribute) {
boolean eventValue = false;
Object value = event.getNewValue();
if (value instanceof Boolean) {
eventValue = ((Boolean) value).booleanValue();
} else if (IPreferenceStore.TRUE.equals(value)) {
eventValue = true;
}
TextAttribute oldAttr = highlighting.getTextAttribute();
boolean activeValue = (oldAttr.getStyle() & styleAttribute) == styleAttribute;
if (activeValue != eventValue) {
highlighting.setTextAttribute(new TextAttribute(oldAttr
.getForeground(), oldAttr.getBackground(),
eventValue ? (oldAttr.getStyle() | styleAttribute)
: (oldAttr.getStyle() & ~styleAttribute)));
}
}
/** Add a color with the given key.
*
* @param colorKey The color key.
* @see #_removeColor(String)
*/
private void _addColor(String colorKey) {
if ((_colorManager != null) && (colorKey != null)
&& (_colorManager.getColor(colorKey) == null)) {
RGB rgb = PreferenceConverter.getColor(_preferenceStore, colorKey);
if (_colorManager instanceof IColorManagerExtension) {
IColorManagerExtension ext = (IColorManagerExtension) _colorManager;
ext.unbindColor(colorKey);
ext.bindColor(colorKey, rgb);
}
}
}
/** Disable the semantic highlightings.
*
* @see #_enable()
*/
private void _disable() {
if (_presenter != null) {
_presenter.setCanceled(true);
}
if (_presenter != null) {
_presenter.uninstall();
_presenter = null;
}
_editor.removeJavaReconcileListener(this);
if (_semanticHighlightings != null) {
_disposeHighlightings();
}
_enabled = false;
}
/** Dispose the resources used by the semantic highlightings.
*/
private void _disposeHighlightings() {
for (int i = 0, n = _semanticHighlightings.length; i < n; i++) {
_removeColor(_semanticHighlightings[i].getColorPreferenceKey());
}
_semanticHighlightings = null;
_highlightings = null;
}
/** Enable the semantic highlightings.
*
* @return true if the highlightings are enabled successfully; false,
* otherwise.
* @see #_disable()
*/
private boolean _enable() {
if (_enabled) {
return true;
}
JavaSourceViewer viewer = (JavaSourceViewer) _editor.getViewer();
if (viewer != null) {
_initializeHighlightings();
final String JAVA_PARTITIONING = "___java_partitioning";
_configuration = new JavaSourceViewerConfiguration(_colorManager,
_preferenceStore, _editor, JAVA_PARTITIONING);
_presentationReconciler = (JavaPresentationReconciler) _configuration
.getPresentationReconciler(viewer);
_presenter = new SemanticHighlightingPresenter();
_presenter.install(viewer, _presentationReconciler);
_jobSemanticHighlightings = _semanticHighlightings;
_editor.addJavaReconcileListener(this);
_enabled = true;
return true;
} else {
return false;
}
}
/** Handle a property change evant.
*
* @param event The event of the change.
* @see #_adaptToEnablementChange(ptolemy.backtrack.eclipse.plugin.editor.SemanticHighlightingReconciler.HighlightingStyle, PropertyChangeEvent)
* @see #_adaptToTextForegroundChange(ptolemy.backtrack.eclipse.plugin.editor.SemanticHighlightingReconciler.HighlightingStyle, PropertyChangeEvent)
* @see #_adaptToTextStyleChange(ptolemy.backtrack.eclipse.plugin.editor.SemanticHighlightingReconciler.HighlightingStyle, PropertyChangeEvent, int)
*/
private void _handlePropertyChangeEvent(PropertyChangeEvent event) {
if (_preferenceStore == null) {
return; // Uninstalled during event notification
}
if (SemanticHighlightings.affectsEnablement(_preferenceStore, event)) {
if (isEnabled()) {
if (_enable()) {
_scheduleJob();
} else {
return;
}
} else {
_disable();
}
}
if (!isEnabled() || _semanticHighlightings == null) {
return;
}
for (int i = 0, n = _semanticHighlightings.length; i < n; i++) {
SemanticHighlighting semanticHighlighting = _semanticHighlightings[i];
String colorKey = semanticHighlighting.getColorPreferenceKey();
if (colorKey.equals(event.getProperty())) {
_adaptToTextForegroundChange(_highlightings[i], event);
_presenter.highlightingStyleChanged(_highlightings[i]);
continue;
}
String boldKey = semanticHighlighting.getBoldPreferenceKey();
if (boldKey.equals(event.getProperty())) {
_adaptToTextStyleChange(_highlightings[i], event, SWT.BOLD);
_presenter.highlightingStyleChanged(_highlightings[i]);
continue;
}
String italicKey = semanticHighlighting.getItalicPreferenceKey();
if (italicKey.equals(event.getProperty())) {
_adaptToTextStyleChange(_highlightings[i], event, SWT.ITALIC);
_presenter.highlightingStyleChanged(_highlightings[i]);
continue;
}
String enabledKey = semanticHighlighting.getEnabledPreferenceKey();
if (enabledKey.equals(event.getProperty())) {
_adaptToEnablementChange(_highlightings[i], event);
_presenter.highlightingStyleChanged(_highlightings[i]);
continue;
}
}
}
/** Initialize semantic highlightings.
*/
private void _initializeHighlightings() {
_semanticHighlightings = SemanticHighlightings
.getSemanticHighlightings();
_highlightings = new HighlightingStyle[_semanticHighlightings.length];
for (int i = 0, n = _semanticHighlightings.length; i < n; i++) {
SemanticHighlighting semanticHighlighting = _semanticHighlightings[i];
String colorKey = semanticHighlighting.getColorPreferenceKey();
_addColor(colorKey);
String boldKey = semanticHighlighting.getBoldPreferenceKey();
int style = _preferenceStore.getBoolean(boldKey) ? SWT.BOLD
: SWT.NORMAL;
String italicKey = semanticHighlighting.getItalicPreferenceKey();
if (_preferenceStore.getBoolean(italicKey)) {
style |= SWT.ITALIC;
}
boolean isEnabled = _preferenceStore
.getBoolean(semanticHighlighting.getEnabledPreferenceKey());
_highlightings[i] = new HighlightingStyle(new TextAttribute(
_colorManager.getColor(PreferenceConverter.getColor(
_preferenceStore, colorKey)), null, style),
isEnabled);
}
}
/** Reconcile the positions of the AST subtrees.
*
* @param subtrees The subtrees to be reconciled.
*/
private void _reconcilePositions(ASTNode[] subtrees) {
for (int i = 0, n = subtrees.length; i < n; i++) {
subtrees[i].accept(_collector);
}
List