KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > javaeditor > SemanticHighlightingReconciler


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.jdt.internal.ui.javaeditor;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.List JavaDoc;
16
17 import org.eclipse.core.runtime.IProgressMonitor;
18 import org.eclipse.core.runtime.IStatus;
19 import org.eclipse.core.runtime.Status;
20 import org.eclipse.core.runtime.jobs.Job;
21
22 import org.eclipse.swt.widgets.Display;
23 import org.eclipse.swt.widgets.Shell;
24
25 import org.eclipse.jface.text.IDocument;
26 import org.eclipse.jface.text.ITextInputListener;
27 import org.eclipse.jface.text.Position;
28 import org.eclipse.jface.text.TextPresentation;
29 import org.eclipse.jface.text.source.ISourceViewer;
30
31 import org.eclipse.ui.IWorkbenchPartSite;
32
33 import org.eclipse.jdt.core.IJavaElement;
34 import org.eclipse.jdt.core.dom.ASTNode;
35 import org.eclipse.jdt.core.dom.BooleanLiteral;
36 import org.eclipse.jdt.core.dom.CharacterLiteral;
37 import org.eclipse.jdt.core.dom.CompilationUnit;
38 import org.eclipse.jdt.core.dom.Expression;
39 import org.eclipse.jdt.core.dom.NumberLiteral;
40 import org.eclipse.jdt.core.dom.SimpleName;
41
42 import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
43
44 import org.eclipse.jdt.internal.ui.JavaPlugin;
45 import org.eclipse.jdt.internal.ui.javaeditor.SemanticHighlightingManager.HighlightedPosition;
46 import org.eclipse.jdt.internal.ui.javaeditor.SemanticHighlightingManager.Highlighting;
47 import org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener;
48
49
50 /**
51  * Semantic highlighting reconciler - Background thread implementation.
52  *
53  * @since 3.0
54  */

55 public class SemanticHighlightingReconciler implements IJavaReconcilingListener, ITextInputListener {
56
57     /**
58      * Collects positions from the AST.
59      */

60     private class PositionCollector extends GenericVisitor {
61
62         /** The semantic token */
63         private SemanticToken fToken= new SemanticToken();
64
65         /*
66          * @see org.eclipse.jdt.internal.corext.dom.GenericVisitor#visitNode(org.eclipse.jdt.core.dom.ASTNode)
67          */

68         protected boolean visitNode(ASTNode node) {
69             if ((node.getFlags() & ASTNode.MALFORMED) == ASTNode.MALFORMED) {
70                 retainPositions(node.getStartPosition(), node.getLength());
71                 return false;
72             }
73             return true;
74         }
75         
76         /*
77          * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.BooleanLiteral)
78          */

79         public boolean visit(BooleanLiteral node) {
80             return visitLiteral(node);
81         }
82         
83         /*
84          * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.CharacterLiteral)
85          */

86         public boolean visit(CharacterLiteral node) {
87             return visitLiteral(node);
88         }
89         
90         /*
91          * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.NumberLiteral)
92          */

93         public boolean visit(NumberLiteral node) {
94             return visitLiteral(node);
95         }
96         
97         private boolean visitLiteral(Expression node) {
98             fToken.update(node);
99             for (int i= 0, n= fJobSemanticHighlightings.length; i < n; i++) {
100                 SemanticHighlighting semanticHighlighting= fJobSemanticHighlightings[i];
101                 if (fJobHighlightings[i].isEnabled() && semanticHighlighting.consumesLiteral(fToken)) {
102                     int offset= node.getStartPosition();
103                     int length= node.getLength();
104                     if (offset > -1 && length > 0)
105                         addPosition(offset, length, fJobHighlightings[i]);
106                     break;
107                 }
108             }
109             fToken.clear();
110             return false;
111         }
112
113         /*
114          * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SimpleName)
115          */

116         public boolean visit(SimpleName node) {
117             fToken.update(node);
118             for (int i= 0, n= fJobSemanticHighlightings.length; i < n; i++) {
119                 SemanticHighlighting semanticHighlighting= fJobSemanticHighlightings[i];
120                 if (fJobHighlightings[i].isEnabled() && semanticHighlighting.consumes(fToken)) {
121                     int offset= node.getStartPosition();
122                     int length= node.getLength();
123                     if (offset > -1 && length > 0)
124                         addPosition(offset, length, fJobHighlightings[i]);
125                     break;
126                 }
127             }
128             fToken.clear();
129             return false;
130         }
131
132         /**
133          * Add a position with the given range and highlighting iff it does not exist already.
134          * @param offset The range offset
135          * @param length The range length
136          * @param highlighting The highlighting
137          */

138         private void addPosition(int offset, int length, Highlighting highlighting) {
139             boolean isExisting= false;
140             // TODO: use binary search
141
for (int i= 0, n= fRemovedPositions.size(); i < n; i++) {
142                 HighlightedPosition position= (HighlightedPosition) fRemovedPositions.get(i);
143                 if (position == null)
144                     continue;
145                 if (position.isEqual(offset, length, highlighting)) {
146                     isExisting= true;
147                     fRemovedPositions.set(i, null);
148                     fNOfRemovedPositions--;
149                     break;
150                 }
151             }
152
153             if (!isExisting) {
154                 Position position= fJobPresenter.createHighlightedPosition(offset, length, highlighting);
155                 fAddedPositions.add(position);
156             }
157         }
158
159         /**
160          * Retain the positions completely contained in the given range.
161          * @param offset The range offset
162          * @param length The range length
163          */

164         private void retainPositions(int offset, int length) {
165             // TODO: use binary search
166
for (int i= 0, n= fRemovedPositions.size(); i < n; i++) {
167                 HighlightedPosition position= (HighlightedPosition) fRemovedPositions.get(i);
168                 if (position != null && position.isContained(offset, length)) {
169                     fRemovedPositions.set(i, null);
170                     fNOfRemovedPositions--;
171                 }
172             }
173         }
174     }
175
176     /** Position collector */
177     private PositionCollector fCollector= new PositionCollector();
178
179     /** The Java editor this semantic highlighting reconciler is installed on */
180     private JavaEditor fEditor;
181     /** The source viewer this semantic highlighting reconciler is installed on */
182     private ISourceViewer fSourceViewer;
183     /** The semantic highlighting presenter */
184     private SemanticHighlightingPresenter fPresenter;
185     /** Semantic highlightings */
186     private SemanticHighlighting[] fSemanticHighlightings;
187     /** Highlightings */
188     private Highlighting[] fHighlightings;
189
190     /** Background job's added highlighted positions */
191     private List JavaDoc fAddedPositions= new ArrayList JavaDoc();
192     /** Background job's removed highlighted positions */
193     private List JavaDoc fRemovedPositions= new ArrayList JavaDoc();
194     /** Number of removed positions */
195     private int fNOfRemovedPositions;
196
197     /** Background job */
198     private Job fJob;
199     /** Background job lock */
200     private final Object JavaDoc fJobLock= new Object JavaDoc();
201     /**
202      * Reconcile operation lock.
203      * @since 3.2
204      */

205     private final Object JavaDoc fReconcileLock= new Object JavaDoc();
206     /**
207      * <code>true</code> if any thread is executing
208      * <code>reconcile</code>, <code>false</code> otherwise.
209      * @since 3.2
210      */

211     private boolean fIsReconciling= false;
212
213     /** The semantic highlighting presenter - cache for background thread, only valid during {@link #reconciled(CompilationUnit, boolean, IProgressMonitor)} */
214     private SemanticHighlightingPresenter fJobPresenter;
215     /** Semantic highlightings - cache for background thread, only valid during {@link #reconciled(CompilationUnit, boolean, IProgressMonitor)} */
216     private SemanticHighlighting[] fJobSemanticHighlightings;
217     /** Highlightings - cache for background thread, only valid during {@link #reconciled(CompilationUnit, boolean, IProgressMonitor)} */
218     private Highlighting[] fJobHighlightings;
219
220     /*
221      * @see org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener#aboutToBeReconciled()
222      */

223     public void aboutToBeReconciled() {
224         // Do nothing
225
}
226
227     /*
228      * @see org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener#reconciled(CompilationUnit, boolean, IProgressMonitor)
229      */

230     public void reconciled(CompilationUnit ast, boolean forced, IProgressMonitor progressMonitor) {
231         // ensure at most one thread can be reconciling at any time
232
synchronized (fReconcileLock) {
233             if (fIsReconciling)
234                 return;
235             else
236                 fIsReconciling= true;
237         }
238         fJobPresenter= fPresenter;
239         fJobSemanticHighlightings= fSemanticHighlightings;
240         fJobHighlightings= fHighlightings;
241         
242         try {
243             if (fJobPresenter == null || fJobSemanticHighlightings == null || fJobHighlightings == null)
244                 return;
245             
246             fJobPresenter.setCanceled(progressMonitor.isCanceled());
247             
248             if (ast == null || fJobPresenter.isCanceled())
249                 return;
250             
251             ASTNode[] subtrees= getAffectedSubtrees(ast);
252             if (subtrees.length == 0)
253                 return;
254             
255             startReconcilingPositions();
256             
257             if (!fJobPresenter.isCanceled())
258                 reconcilePositions(subtrees);
259             
260             TextPresentation textPresentation= null;
261             if (!fJobPresenter.isCanceled())
262                 textPresentation= fJobPresenter.createPresentation(fAddedPositions, fRemovedPositions);
263             
264             if (!fJobPresenter.isCanceled())
265                 updatePresentation(textPresentation, fAddedPositions, fRemovedPositions);
266             
267             stopReconcilingPositions();
268         } finally {
269             fJobPresenter= null;
270             fJobSemanticHighlightings= null;
271             fJobHighlightings= null;
272             synchronized (fReconcileLock) {
273                 fIsReconciling= false;
274             }
275         }
276     }
277
278     /**
279      * @param node Root node
280      * @return Array of subtrees that may be affected by past document changes
281      */

282     private ASTNode[] getAffectedSubtrees(ASTNode node) {
283         // TODO: only return nodes which are affected by document changes - would require an 'anchor' concept for taking distant effects into account
284
return new ASTNode[] { node };
285     }
286
287     /**
288      * Start reconciling positions.
289      */

290     private void startReconcilingPositions() {
291         fJobPresenter.addAllPositions(fRemovedPositions);
292         fNOfRemovedPositions= fRemovedPositions.size();
293     }
294
295     /**
296      * Reconcile positions based on the AST subtrees
297      *
298      * @param subtrees the AST subtrees
299      */

300     private void reconcilePositions(ASTNode[] subtrees) {
301         // FIXME: remove positions not covered by subtrees
302
for (int i= 0, n= subtrees.length; i < n; i++)
303             subtrees[i].accept(fCollector);
304         List JavaDoc oldPositions= fRemovedPositions;
305         List JavaDoc newPositions= new ArrayList JavaDoc(fNOfRemovedPositions);
306         for (int i= 0, n= oldPositions.size(); i < n; i ++) {
307             Object JavaDoc current= oldPositions.get(i);
308             if (current != null)
309                 newPositions.add(current);
310         }
311         fRemovedPositions= newPositions;
312     }
313
314     /**
315      * Update the presentation.
316      *
317      * @param textPresentation the text presentation
318      * @param addedPositions the added positions
319      * @param removedPositions the removed positions
320      */

321     private void updatePresentation(TextPresentation textPresentation, List JavaDoc addedPositions, List JavaDoc removedPositions) {
322         Runnable JavaDoc runnable= fJobPresenter.createUpdateRunnable(textPresentation, addedPositions, removedPositions);
323         if (runnable == null)
324             return;
325
326         JavaEditor editor= fEditor;
327         if (editor == null)
328             return;
329
330         IWorkbenchPartSite site= editor.getSite();
331         if (site == null)
332             return;
333
334         Shell shell= site.getShell();
335         if (shell == null || shell.isDisposed())
336             return;
337
338         Display display= shell.getDisplay();
339         if (display == null || display.isDisposed())
340             return;
341
342         display.asyncExec(runnable);
343     }
344
345     /**
346      * Stop reconciling positions.
347      */

348     private void stopReconcilingPositions() {
349         fRemovedPositions.clear();
350         fNOfRemovedPositions= 0;
351         fAddedPositions.clear();
352     }
353
354     /**
355      * Install this reconciler on the given editor, presenter and highlightings.
356      * @param editor the editor
357      * @param sourceViewer the source viewer
358      * @param presenter the semantic highlighting presenter
359      * @param semanticHighlightings the semantic highlightings
360      * @param highlightings the highlightings
361      */

362     public void install(JavaEditor editor, ISourceViewer sourceViewer, SemanticHighlightingPresenter presenter, SemanticHighlighting[] semanticHighlightings, Highlighting[] highlightings) {
363         fPresenter= presenter;
364         fSemanticHighlightings= semanticHighlightings;
365         fHighlightings= highlightings;
366
367         fEditor= editor;
368         fSourceViewer= sourceViewer;
369
370         if (fEditor instanceof CompilationUnitEditor) {
371             ((CompilationUnitEditor)fEditor).addReconcileListener(this);
372         } else if (fEditor == null) {
373             fSourceViewer.addTextInputListener(this);
374             scheduleJob();
375         }
376     }
377
378     /**
379      * Uninstall this reconciler from the editor
380      */

381     public void uninstall() {
382         if (fPresenter != null)
383             fPresenter.setCanceled(true);
384
385         if (fEditor != null) {
386             if (fEditor instanceof CompilationUnitEditor)
387                 ((CompilationUnitEditor)fEditor).removeReconcileListener(this);
388             else
389                 fSourceViewer.removeTextInputListener(this);
390             fEditor= null;
391         }
392
393         fSourceViewer= null;
394         fSemanticHighlightings= null;
395         fHighlightings= null;
396         fPresenter= null;
397     }
398
399     /**
400      * Schedule a background job for retrieving the AST and reconciling the Semantic Highlighting model.
401      */

402     private void scheduleJob() {
403         final IJavaElement element= fEditor.getInputJavaElement();
404
405         synchronized (fJobLock) {
406             final Job oldJob= fJob;
407             if (fJob != null) {
408                 fJob.cancel();
409                 fJob= null;
410             }
411             
412             if (element != null) {
413                 fJob= new Job(JavaEditorMessages.SemanticHighlighting_job) {
414                     protected IStatus run(IProgressMonitor monitor) {
415                         if (oldJob != null) {
416                             try {
417                                 oldJob.join();
418                             } catch (InterruptedException JavaDoc e) {
419                                 JavaPlugin.log(e);
420                                 return Status.CANCEL_STATUS;
421                             }
422                         }
423                         if (monitor.isCanceled())
424                             return Status.CANCEL_STATUS;
425                         CompilationUnit ast= JavaPlugin.getDefault().getASTProvider().getAST(element, ASTProvider.WAIT_YES, monitor);
426                         reconciled(ast, false, monitor);
427                         synchronized (fJobLock) {
428                             // allow the job to be gc'ed
429
if (fJob == this)
430                                 fJob= null;
431                         }
432                         return Status.OK_STATUS;
433                     }
434                 };
435                 fJob.setSystem(true);
436                 fJob.setPriority(Job.DECORATE);
437                 fJob.schedule();
438             }
439         }
440     }
441
442     /*
443      * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
444      */

445     public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
446         synchronized (fJobLock) {
447             if (fJob != null) {
448                 fJob.cancel();
449                 fJob= null;
450             }
451         }
452     }
453
454     /*
455      * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
456      */

457     public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
458         if (newInput != null)
459             scheduleJob();
460     }
461     
462     /**
463      * Refreshes the highlighting.
464      *
465      * @since 3.2
466      */

467     public void refresh() {
468         scheduleJob();
469     }
470 }
471
Popular Tags