/******************************************************************************* * Copyright (c) 2000, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * eBay Inc - Modification *******************************************************************************/ package org.eclipse.vjet.eclipse.internal.ui.editor.semantic.highlighting; 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.dltk.mod.ast.declarations.ModuleDeclaration; import org.eclipse.dltk.mod.core.IModelElement; import org.eclipse.dltk.mod.core.ISourceModule; import org.eclipse.dltk.mod.internal.ui.editor.DLTKEditorMessages; import org.eclipse.dltk.mod.internal.ui.editor.ScriptEditor; import org.eclipse.dltk.mod.internal.ui.editor.semantic.highlighting.SemanticHighlightingPresenter; import org.eclipse.dltk.mod.internal.ui.text.IScriptReconcilingListener; import org.eclipse.dltk.mod.ui.DLTKUIPlugin; import org.eclipse.dltk.mod.ui.editor.highlighting.HighlightedPosition; import org.eclipse.dltk.mod.ui.editor.highlighting.HighlightingStyle; import org.eclipse.dltk.mod.ui.editor.highlighting.ISemanticHighlighter; import org.eclipse.dltk.mod.ui.editor.highlighting.SemanticHighlighting; import org.eclipse.dltk.mod.ui.text.ScriptTextTools; 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.ISourceViewer; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.vjet.dsf.jst.IJstNode; import org.eclipse.vjet.dsf.jst.JstSource; import org.eclipse.vjet.dsf.jst.declaration.JstMethod; import org.eclipse.vjet.eclipse.core.IVjoSourceModule; import org.eclipse.vjet.eclipse.core.ts.GenericVisitor; /** * Semantic highlighting reconciler - Background thread implementation. * * @since 3.0 */ public class VjoSemanticHighlightingReconciler implements IScriptReconcilingListener, ITextInputListener { /** The Java editor this semantic highlighting reconciler is installed on */ private ScriptEditor fEditor; /** The source viewer this semantic highlighting reconciler is installed on */ private ISourceViewer fSourceViewer; /** The semantic highlighting presenter */ private SemanticHighlightingPresenter fPresenter; /** Semantic highlightings */ private SemanticHighlighting[] fSemanticHighlightings; /** Highlightings */ private HighlightingStyle[] fHighlightings; /** 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; /** 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; /** * The semantic highlighting presenter - cache for background thread, only * valid during * {@link #reconciled(ModuleDeclaration, boolean, IProgressMonitor)} */ private SemanticHighlightingPresenter fJobPresenter; /** * Semantic highlightings - cache for background thread, only valid during * {@link #reconciled(ModuleDeclaration, boolean, IProgressMonitor)} */ private SemanticHighlighting[] fJobSemanticHighlightings; /** * Highlightings - cache for background thread, only valid during * {@link #reconciled(ModuleDeclaration, boolean, IProgressMonitor)} */ private HighlightingStyle[] fJobHighlightings; private ISemanticHighlighter positionUpdater; /** Position collector */ private PositionCollector fCollector = new PositionCollector(); /* * @see org.eclipse.jdt.internal.ui.text.Script.IScriptReconcilingListener# * aboutToBeReconciled() */ public void aboutToBeReconciled() { // Do nothing } private class PositionCollector extends GenericVisitor { /** The semantic token */ private SemanticToken fToken = new SemanticToken(); // public boolean visit(IJstNode node) { // // if (node instanceof JstName) { // visitNameNode((JstName) node); // } // // return true; // } @Override public void visit(JstMethod node) { fToken.update(node); for (int i = 0, n = fJobSemanticHighlightings.length; i < n; i++) { VjoSemanticHighlighting semanticHighlighting = (VjoSemanticHighlighting) fJobSemanticHighlightings[i]; if (fJobHighlightings[i].isEnabled() && semanticHighlighting.consumes(fToken)) { JstSource source = node.getName().getSource(); if(source==null){ continue; } int offset = source.getStartOffSet(); int length = source.getLength(); if (offset > -1 && length > 0) addPosition(offset, length, fJobHighlightings[i]); break; } } fToken.clear(); } // private boolean visitLiteral(Expression node) { // fToken.update(node); // for (int i = 0, n = fJobSemanticHighlightings.length; i < n; i++) { // SemanticHighlighting semanticHighlighting = // fJobSemanticHighlightings[i]; // if (fJobHighlightings[i].isEnabled() // && semanticHighlighting.consumesLiteral(fToken)) { // int offset = node.getStartPosition(); // int length = node.getLength(); // if (offset > -1 && length > 0) // addPosition(offset, length, fJobHighlightings[i]); // break; // } // } // fToken.clear(); // return false; // } // // /** * Add a position with the given range and highlighting iff it does not * exist already. * * @param offset * The range offset * @param length * The range length * @param highlighting * The highlighting */ private void addPosition(int offset, int length, HighlightingStyle 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); } } // // /** // * Retain the positions completely contained in the given range. // * // * @param offset // * The range offset // * @param length // * The range length // */ // private void retainPositions(int offset, int length) { // // TODO: use binary search // for (int i = 0, n = fRemovedPositions.size(); i < n; i++) { // HighlightedPosition position = (HighlightedPosition) // fRemovedPositions // .get(i); // if (position != null && position.isContained(offset, length)) { // fRemovedPositions.set(i, null); // fNOfRemovedPositions--; // } // } // } } /* * @see org.eclipse.jdt.internal.ui.text.Script.IScriptReconcilingListener#reconciled * (ModuleDeclaration, boolean, IProgressMonitor) */ public void reconciled(ISourceModule ast, boolean forced, IProgressMonitor progressMonitor) { // if (positionUpdater == null) // return; // // 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 { // // long t0 = System.currentTimeMillis(); // if (fJobPresenter == null || fJobSemanticHighlightings == null || // fJobHighlightings == null) // return; // // fJobPresenter.setCanceled(progressMonitor.isCanceled()); // // if (ast == null || fJobPresenter.isCanceled()) // return; // // HighlightedPosition[] added = HighlightedPosition.NO_POSITIONS; // HighlightedPosition[] removed = HighlightedPosition.NO_POSITIONS; // if (!fJobPresenter.isCanceled()) { // final List currentPositions = new ArrayList(); // fJobPresenter.addAllPositions(currentPositions); // final UpdateResult result = // positionUpdater.reconcile((org.eclipse.dltk.mod.compiler.env.ISourceModule) // ast, currentPositions); // added = result.addedPositions; // removed = result.removedPositions; // } // // // // // //ASTNode[] subtrees= getAffectedSubtrees(ast); // // if (subtrees.length == 0) // // return; // // startReconcilingPositions(); // // if (!fJobPresenter.isCanceled()) // reconcilePositions(((VjoSourceModule)ast).getJstType()); // // // TextPresentation textPresentation= null; // // if (!fJobPresenter.isCanceled()) // // textPresentation= // fJobPresenter.createPresentation(fAddedPositions, fRemovedPositions); // // // // if (!fJobPresenter.isCanceled()) // // updatePresentation(textPresentation, fAddedPositions, // fRemovedPositions); // // // stopReconcilingPositions(); // // // // // if (added.length != 0 || removed.length != 0) { // if (!fJobPresenter.isCanceled()) { // final TextPresentation textPresentation = // fJobPresenter.createPresentation(added, removed); // if (!fJobPresenter.isCanceled()) // updatePresentation(textPresentation, added, removed); // } // } // // // long t1 = System.currentTimeMillis(); // // System.out.println(t1 - t0); // // } finally { // fJobPresenter = null; // fJobSemanticHighlightings = null; // fJobHighlightings = null; // synchronized (fReconcileLock) { // fIsReconciling = false; // } // } 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(((IVjoSourceModule) ast).getJstType()); TextPresentation textPresentation = null; HighlightedPosition[] addedPositions = (HighlightedPosition[]) fAddedPositions .toArray(new HighlightedPosition[fAddedPositions.size()]); HighlightedPosition[] removedPositions = (HighlightedPosition[]) fRemovedPositions .toArray(new HighlightedPosition[fRemovedPositions.size()]); if (!fJobPresenter.isCanceled()) textPresentation = fJobPresenter.createPresentation( addedPositions, removedPositions); if (!fJobPresenter.isCanceled()) updatePresentation(textPresentation, addedPositions, removedPositions); stopReconcilingPositions(); } finally { fJobPresenter = null; fJobSemanticHighlightings = null; fJobHighlightings = null; synchronized (fReconcileLock) { fIsReconciling = false; } } } /** * Start reconciling positions. */ private void startReconcilingPositions() { fJobPresenter.addAllPositions(fRemovedPositions); fNOfRemovedPositions = fRemovedPositions.size(); } /** * Stop reconciling positions. */ private void stopReconcilingPositions() { fRemovedPositions.clear(); fNOfRemovedPositions = 0; fAddedPositions.clear(); } /** * Reconcile positions based on the AST subtrees * * @param subtrees * the AST subtrees */ private void reconcilePositions(IJstNode jstNode) { jstNode.accept(fCollector); List oldPositions = fRemovedPositions; List newPositions = new ArrayList(fNOfRemovedPositions); for (int i = 0, n = oldPositions.size(); i < n; i++) { Object current = oldPositions.get(i); if (current != null) newPositions.add(current); } fRemovedPositions = newPositions; } /** * Update the presentation. * * @param textPresentation * the text presentation * @param addedPositions * the added positions * @param removedPositions * the removed positions */ private void updatePresentation(TextPresentation textPresentation, HighlightedPosition[] addedPositions, HighlightedPosition[] removedPositions) { Runnable runnable = fJobPresenter.createUpdateRunnable( textPresentation, addedPositions, removedPositions); if (runnable == null) return; ScriptEditor editor = fEditor; if (editor == null) return; 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); } public void install(ScriptEditor editor, ISourceViewer sourceViewer, SemanticHighlightingPresenter presenter, SemanticHighlighting[] semanticHighlightings, HighlightingStyle[] highlightings) { fPresenter = presenter; fSemanticHighlightings = semanticHighlightings; fHighlightings = highlightings; ScriptTextTools textTools = editor.getTextTools(); if (textTools != null) { this.positionUpdater = textTools.getSemanticPositionUpdater(); this.positionUpdater.initialize(fPresenter, fHighlightings); } fEditor = editor; fSourceViewer = sourceViewer; // if (fEditor != null) { fEditor.addReconcileListener(this); // } else { // fSourceViewer.addTextInputListener(this); // scheduleJob(); // } } /** * Uninstall this reconciler from the editor */ public void uninstall() { if (fPresenter != null) fPresenter.setCanceled(true); if (fEditor != null) { fEditor.removeReconcileListener(this); fEditor = null; } else { fSourceViewer.removeTextInputListener(this); } fSourceViewer = null; fSemanticHighlightings = null; fHighlightings = null; fPresenter = null; } /** * Schedule a background job for retrieving the AST and reconciling the * Semantic Highlighting model. */ private void scheduleJob() { final IModelElement element = fEditor.getInputModelElement(); synchronized (fJobLock) { final Job oldJob = fJob; if (fJob != null) { fJob.cancel(); fJob = null; } if (element != null) { fJob = new Job(DLTKEditorMessages.SemanticHighlighting_job) { protected IStatus run(IProgressMonitor monitor) { if (oldJob != null) { try { oldJob.join(); } catch (InterruptedException e) { DLTKUIPlugin.log(e); return Status.CANCEL_STATUS; } } if (monitor.isCanceled()) return Status.CANCEL_STATUS; final ISourceModule code = DLTKUIPlugin.getDefault() .getWorkingCopyManager().getWorkingCopy( fEditor.getEditorInput()); reconciled(code, 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(); } }