package de.ovgu.cide.language.jdt.editor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
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.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener;
import org.eclipse.jdt.ui.SharedASTProvider;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextInputListener;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchPartSite;
import de.ovgu.cide.configuration.jdt.JDTColorManagerBridge;
import de.ovgu.cide.editor.CodeSegment;
import de.ovgu.cide.language.jdt.editor.inlineprojection.ColoredInlineProjectionAnnotation;
import de.ovgu.cide.language.jdt.editor.inlineprojection.InlineProjectionAnnotationModel;
import de.ovgu.cide.language.jdt.editor.inlineprojection.InlineProjectionJavaViewer;
@SuppressWarnings(value={"restriction","unchecked"})
public class ColoredHighlightingReconciler implements IJavaReconcilingListener,
ITextInputListener {
/** The Java editor this semantic highlighting reconciler is installed on */
private ColoredCompilationUnitEditor fEditor;
/** The source viewer this semantic highlighting reconciler is installed on */
private ISourceViewer fSourceViewer;
/** The semantic highlighting presenter */
private ColoredHighlightingPresenter fPresenter;
/** Semantic highlightings */
// private SemanticHighlighting[] fSemanticHighlightings;
/** Highlightings */
// private Highlighting[] fHighlightings;
/** Background job's added highlighted positions */
private List fAddedPositions = new ArrayList();
/** Background job's removed highlighted positions */
private List fRemovedPositions = new ArrayList();
/** Number of removed positions */
// private int fNOfRemovedPositions;
/** Background job */
private Job fJob;
/** Background job lock */
private final Object fJobLock = new Object();
/**
* Reconcile operation lock.
*
* @since 3.2
*/
private final Object fReconcileLock = new Object();
/**
* <code>true</code> if any thread is executing <code>reconcile</code>,
* <code>false</code> otherwise.
*
* @since 3.2
*/
private boolean fIsReconciling = false;
/**
* The semantic highlighting presenter - cache for background thread, only
* valid during
* {@link #reconciled(CompilationUnit, boolean, IProgressMonitor)}
*/
private ColoredHighlightingPresenter fJobPresenter;
private JDTColorManagerBridge colorManager;
/**
* Semantic highlightings - cache for background thread, only valid during
* {@link #reconciled(CompilationUnit, boolean, IProgressMonitor)}
*/
// private SemanticHighlighting[] fJobSemanticHighlightings;
/**
* Highlightings - cache for background thread, only valid during
* {@link #reconciled(CompilationUnit, boolean, IProgressMonitor)}
*/
// private Highlighting[] fJobHighlightings;
/*
* @see org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener#aboutToBeReconciled()
*/
public void aboutToBeReconciled() {
// Do nothing
}
/*
* @see org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener#reconciled(CompilationUnit,
* boolean, IProgressMonitor)
*/
public void reconciled(CompilationUnit ast, boolean forced,
IProgressMonitor progressMonitor) {
// ensure at most one thread can be reconciling at any time
synchronized (fReconcileLock) {
if (fIsReconciling)
return;
else
fIsReconciling = true;
}
fJobPresenter = fPresenter;
// fJobSemanticHighlightings= fSemanticHighlightings;
// fJobHighlightings= fHighlightings;
try {
if (fJobPresenter == null)// || fJobSemanticHighlightings == null
// || fJobHighlightings == null)
return;
fJobPresenter.setCanceled(progressMonitor.isCanceled());
if (ast == null || fJobPresenter.isCanceled())
return;
ASTNode[] subtrees = getAffectedSubtrees(ast);
if (subtrees.length == 0)
return;
startReconcilingPositions();
if (!fJobPresenter.isCanceled())
reconcilePositions(subtrees);
TextPresentation textPresentation = null;
if (!fJobPresenter.isCanceled())
textPresentation = fJobPresenter.createPresentation(
fAddedPositions, fRemovedPositions);
if (!fJobPresenter.isCanceled())
updatePresentation(textPresentation, fAddedPositions,
fRemovedPositions);
stopReconcilingPositions();
} finally {
fJobPresenter = null;
// fJobSemanticHighlightings= null;
// fJobHighlightings= null;
synchronized (fReconcileLock) {
fIsReconciling = false;
}
}
}
/**
* @param node
* Root node
* @return Array of subtrees that may be affected by past document changes
*/
private ASTNode[] getAffectedSubtrees(ASTNode node) {
// TODO: only return nodes which are affected by document changes -
// would require an 'anchor' concept for taking distant effects into
// account
return new ASTNode[] { node };
}
/**
* Start reconciling positions.
*/
private void startReconcilingPositions() {
fJobPresenter.addAllPositions(fRemovedPositions);
// fNOfRemovedPositions = fRemovedPositions.size();
}
/**
* Reconcile positions based on the AST subtrees
*
* @param subtrees
* the AST subtrees
*/
private void reconcilePositions(ASTNode[] subtrees) {
List<CodeSegment> list = new ArrayList<CodeSegment>();
for (int i = 0, n = subtrees.length; i < n; i++)
list.addAll(CodeSegmentCalculatorJava.getCodeSegments(subtrees[i],
colorManager));
// subtrees[i].accept(fCollector);
deleteEmptySegments(list);
fAddedPositions.addAll(list);
List oldPositions = fRemovedPositions;
List newPositions = list;
for (int i = 0, n = oldPositions.size(); i < n; i++) {
Object current = oldPositions.get(i);
if (current != null)
newPositions.add(current);
}
fRemovedPositions = newPositions;
}
private void deleteEmptySegments(List<CodeSegment> list) {
for (Iterator i = list.iterator(); i.hasNext();) {
CodeSegment segment = (CodeSegment) i.next();
if (segment.getColors().isEmpty())
i.remove();
}
}
// private void addPosition(int offset, int length, Highlighting
// highlighting) {
// boolean isExisting= false;
// // TODO: use binary search
// for (int i= 0, n= fRemovedPositions.size(); i < n; i++) {
// HighlightedPosition position= (HighlightedPosition)
// fRemovedPositions.get(i);
// if (position == null)
// continue;
// if (position.isEqual(offset, length, highlighting)) {
// isExisting= true;
// fRemovedPositions.set(i, null);
// fNOfRemovedPositions--;
// break;
// }
// }
//
// if (!isExisting) {
// Position position= fJobPresenter.createHighlightedPosition(offset,
// length, highlighting);
// fAddedPositions.add(position);
// }
// }
private List<ColoredInlineProjectionAnnotation> oldAnnotations=new ArrayList<ColoredInlineProjectionAnnotation>();
/**
* Update the presentation.
*
* @param textPresentation
* the text presentation
* @param addedPositions
* the added positions
* @param removedPositions
* the removed positions
*/
private void updatePresentation(TextPresentation textPresentation,
List<CodeSegment> addedPositions, List<CodeSegment> removedPositions) {
// System.out.println("Reconciled: " + textPresentation + " "
// + addedPositions + " " + removedPositions);
Runnable runnable = fJobPresenter.createUpdateRunnable(
textPresentation, addedPositions, removedPositions);
if (runnable == null)
return;
ColoredCompilationUnitEditor editor = fEditor;
if (editor == null)
return;
// updateInlineProjectionAnnotations(addedPositions, editor);TODO disabled inline projection
IWorkbenchPartSite site = editor.getSite();
if (site == null)
return;
Shell shell = site.getShell();
if (shell == null || shell.isDisposed())
return;
Display display = shell.getDisplay();
if (display == null || display.isDisposed())
return;
display.asyncExec(runnable);
}
protected void updateInlineProjectionAnnotations(List<CodeSegment> addedPositions, ColoredCompilationUnitEditor editor) {
InlineProjectionAnnotationModel annotationModel = ((InlineProjectionJavaViewer) editor
.getViewer()).getInlineProjectionAnnotationModel();
ColoredInlineProjectionAnnotation[] annotations = new ColoredInlineProjectionAnnotation[addedPositions
.size()];
// this will hold the new annotations along
// with their corresponding positions
HashMap newAnnotations = new HashMap();
addedPositions = new ArrayList<CodeSegment>(addedPositions);
ArrayList<ColoredInlineProjectionAnnotation> knownPositions = new ArrayList<ColoredInlineProjectionAnnotation>(
oldAnnotations);
ArrayList<ColoredInlineProjectionAnnotation> savedPositions = new ArrayList<ColoredInlineProjectionAnnotation>();
// move unchanged segments from known to saved (those are not deleted)
for (Iterator<CodeSegment> i = addedPositions.iterator(); i.hasNext();) {
CodeSegment seg = i.next();
for (Iterator<ColoredInlineProjectionAnnotation> a = knownPositions
.iterator(); a.hasNext();) {
ColoredInlineProjectionAnnotation known = a.next();
if (seg.offset == known.getPosition().getOffset()
&& seg.length == known.getPosition().getLength()
&& seg.getColors().equals(known.getColors())) {
i.remove();
a.remove();
savedPositions.add(known);
}
}
}
for (int i = 0; i < addedPositions.size(); i++) {
ColoredInlineProjectionAnnotation annotation = new ColoredInlineProjectionAnnotation();
annotation.setColors(addedPositions.get(i).getColors());
Position pos = new Position(addedPositions.get(i).offset,
addedPositions.get(i).length);
annotation.setPosition(pos);
annotation.adjustCollapsing(editor.getProjectionColorManager().getExpandedColors());
newAnnotations.put(annotation, pos);
annotations[i] = annotation;
}
if (annotationModel != null) {
Annotation[] deletedAnnotations = new Annotation[knownPositions
.size()];
deletedAnnotations = knownPositions.toArray(deletedAnnotations);
annotationModel.modifyAnnotations(deletedAnnotations,
newAnnotations, null);
}
oldAnnotations = savedPositions;
oldAnnotations.addAll(newAnnotations.keySet());
}
/**
* Stop reconciling positions.
*/
private void stopReconcilingPositions() {
fRemovedPositions.clear();
// fNOfRemovedPositions = 0;
fAddedPositions.clear();
}
/**
* Install this reconciler on the given editor, presenter and highlightings.
*
* @param editor
* the editor
* @param sourceViewer
* the source viewer
* @param presenter
* the semantic highlighting presenter
* @param semanticHighlightings
* the semantic highlightings
* @param highlightings
* the highlightings
*/
public void install(ColoredCompilationUnitEditor editor,
ISourceViewer sourceViewer, ColoredHighlightingPresenter presenter) {
fPresenter = presenter;
// fSemanticHighlightings= semanticHighlightings;
// fHighlightings= highlightings;
fEditor = editor;
fSourceViewer = sourceViewer;
if (fEditor instanceof ColoredCompilationUnitEditor) {
((ColoredCompilationUnitEditor) fEditor)
.addReconcileListener2(this);
} else {
fSourceViewer.addTextInputListener(this);
scheduleJob();
}
}
/**
* Uninstall this reconciler from the editor
*/
public void uninstall() {
if (fPresenter != null)
fPresenter.setCanceled(true);
if (fEditor != null) {
if (fEditor instanceof ColoredCompilationUnitEditor)
((ColoredCompilationUnitEditor) fEditor)
.removeReconcileListener2(this);
else
fSourceViewer.removeTextInputListener(this);
fEditor = null;
}
fSourceViewer = null;
// fSemanticHighlightings = null;
// fHighlightings = null;
fPresenter = null;
}
/**
* Schedule a background job for retrieving the AST and reconciling the
* Semantic Highlighting model.
*/
void scheduleJob() {
final IJavaElement element = fEditor.getInputJavaElement();
assert element instanceof ICompilationUnit;
synchronized (fJobLock) {
final Job oldJob = fJob;
if (fJob != null) {
fJob.cancel();
fJob = null;
}
if (element != null) {
fJob = new Job("CodeColoring") {
protected IStatus run(IProgressMonitor monitor) {
if (oldJob != null) {
try {
oldJob.join();
} catch (InterruptedException e) {
JavaPlugin.log(e);
return Status.CANCEL_STATUS;
}
}
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
CompilationUnit ast = JavaPlugin.getDefault()
.getASTProvider().getAST((ICompilationUnit)element,
SharedASTProvider.WAIT_YES, monitor);
reconciled(ast, false, monitor);
synchronized (fJobLock) {
// allow the job to be gc'ed
if (fJob == this)
fJob = null;
}
return Status.OK_STATUS;
}
};
fJob.setSystem(true);
fJob.setPriority(Job.DECORATE);
fJob.schedule();
}
}
}
/*
* @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument,
* org.eclipse.jface.text.IDocument)
*/
public void inputDocumentAboutToBeChanged(IDocument oldInput,
IDocument newInput) {
synchronized (fJobLock) {
if (fJob != null) {
fJob.cancel();
fJob = null;
}
}
}
/*
* @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument,
* org.eclipse.jface.text.IDocument)
*/
public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
if (newInput != null)
scheduleJob();
}
/**
* Refreshes the highlighting.
*
* @since 3.2
*/
public void refresh() {
scheduleJob();
}
public void setColorManager(JDTColorManagerBridge compUnitColorManager) {
this.colorManager = compUnitColorManager;
}
}