bugfix 1198893, syntax coloring gets confused after single line comment with question...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / text / JavaReconciler.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11
12 package net.sourceforge.phpdt.internal.ui.text;
13
14
15 import net.sourceforge.phpdt.core.ElementChangedEvent;
16 import net.sourceforge.phpdt.core.IElementChangedListener;
17 import net.sourceforge.phpdt.core.JavaCore;
18 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
19 import net.sourceforge.phpeclipse.phpeditor.PHPUnitEditor;
20
21 import org.eclipse.core.resources.IMarkerDelta;
22 import org.eclipse.core.resources.IResource;
23 import org.eclipse.core.resources.IResourceChangeEvent;
24 import org.eclipse.core.resources.IResourceChangeListener;
25 import org.eclipse.core.resources.IResourceDelta;
26 import org.eclipse.core.resources.IWorkspace;
27 import org.eclipse.jface.text.IDocument;
28 import org.eclipse.jface.text.ITextViewer;
29 import org.eclipse.jface.text.reconciler.DirtyRegion;
30 import org.eclipse.jface.text.reconciler.MonoReconciler;
31 import org.eclipse.swt.events.ShellAdapter;
32 import org.eclipse.swt.events.ShellEvent;
33 import org.eclipse.swt.events.ShellListener;
34 import org.eclipse.swt.widgets.Control;
35 import org.eclipse.swt.widgets.Shell;
36 import org.eclipse.ui.IEditorInput;
37 import org.eclipse.ui.IFileEditorInput;
38 import org.eclipse.ui.IPartListener;
39 import org.eclipse.ui.IWorkbenchPart;
40 import org.eclipse.ui.IWorkbenchPartSite;
41 import org.eclipse.ui.IWorkbenchWindow;
42 import org.eclipse.ui.texteditor.ITextEditor;
43
44
45 /**
46  * A reconciler that is also activated on editor activation.
47  */
48 public class JavaReconciler extends MonoReconciler {
49         
50         /**
51          * Internal part listener for activating the reconciler.
52          */
53         private class PartListener implements IPartListener {
54                 
55                 /*
56                  * @see org.eclipse.ui.IPartListener#partActivated(org.eclipse.ui.IWorkbenchPart)
57                  */
58                 public void partActivated(IWorkbenchPart part) {
59                         if (part == fTextEditor && hasJavaModelChanged())
60                                 JavaReconciler.this.forceReconciling();
61                 }
62
63                 /*
64                  * @see org.eclipse.ui.IPartListener#partBroughtToTop(org.eclipse.ui.IWorkbenchPart)
65                  */
66                 public void partBroughtToTop(IWorkbenchPart part) {
67                 }
68
69                 /*
70                  * @see org.eclipse.ui.IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart)
71                  */
72                 public void partClosed(IWorkbenchPart part) {
73                 }
74
75                 /*
76                  * @see org.eclipse.ui.IPartListener#partDeactivated(org.eclipse.ui.IWorkbenchPart)
77                  */
78                 public void partDeactivated(IWorkbenchPart part) {
79                         if (part == fTextEditor)
80                                 setJavaModelChanged(false);
81                 }
82
83                 /*
84                  * @see org.eclipse.ui.IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart)
85                  */
86                 public void partOpened(IWorkbenchPart part) {
87                 }
88         }
89         
90         /**
91          * Internal Shell activation listener for activating the reconciler.
92          */
93         private class ActivationListener extends ShellAdapter {
94                 
95                 private Control fControl;
96                 
97                 public ActivationListener(Control control) {
98                         fControl= control;
99                 }
100
101                 /*
102                  * @see org.eclipse.swt.events.ShellListener#shellActivated(org.eclipse.swt.events.ShellEvent)
103                  */
104                 public void shellActivated(ShellEvent e) {
105                         if (!fControl.isDisposed() && fControl.isVisible() && hasJavaModelChanged())
106                                 JavaReconciler.this.forceReconciling();
107                 }
108                 
109                 /*
110                  * @see org.eclipse.swt.events.ShellListener#shellDeactivated(org.eclipse.swt.events.ShellEvent)
111                  */
112                 public void shellDeactivated(ShellEvent e) {
113                         setJavaModelChanged(false);
114                 }
115         }
116         
117         /**
118          * Internal Java element changed listener
119          * 
120          * @since 3.0
121          */
122         private class ElementChangedListener implements IElementChangedListener {
123                 /*
124                  * @see net.sourceforge.phpdt.core.IElementChangedListener#elementChanged(net.sourceforge.phpdt.core.ElementChangedEvent)
125                  */
126                 public void elementChanged(ElementChangedEvent event) {
127                         setJavaModelChanged(true);
128                 }
129         }
130         
131         /**
132          * Internal resource change listener.
133          * 
134          * @since 3.0
135          */
136         class ResourceChangeListener implements IResourceChangeListener {
137                 
138                 private IResource getResource() {
139                         IEditorInput input= fTextEditor.getEditorInput();
140                         if (input instanceof IFileEditorInput) {
141                                 IFileEditorInput fileInput= (IFileEditorInput) input;
142                                 return fileInput.getFile();
143                         }
144                         return null;
145                 }
146                 
147                 /*
148                  * @see IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
149                  */
150                 public void resourceChanged(IResourceChangeEvent e) {
151                         IResourceDelta delta= e.getDelta();
152                         IResource resource= getResource();
153                         if (delta != null && resource != null) {
154                                 IResourceDelta child= delta.findMember(resource.getFullPath());
155                                 if (child != null) {
156                                         IMarkerDelta[] deltas= child.getMarkerDeltas();
157                                         if (deltas.length > 0)
158                                                 forceReconciling();
159                                 }
160                         }
161                 }
162         }
163         
164         
165         /** The reconciler's editor */
166         private ITextEditor fTextEditor;
167         /** The part listener */
168         private IPartListener fPartListener;
169         /** The shell listener */
170         private ShellListener fActivationListener;
171         /**
172          * The mutex that keeps us from running multiple reconcilers on one editor.
173          * TODO remove once we have ensured that there is only one reconciler per editor. 
174          */
175         private Object fMutex;
176         /**
177          * The Java element changed listener.
178          * @since 3.0
179          */
180         private IElementChangedListener fJavaElementChangedListener;
181         /**
182          * Tells whether the Java model sent out a changed event.
183          * @since 3.0
184          */
185         private volatile boolean fHasJavaModelChanged= true;
186         /**
187          * The resource change listener.
188          * @since 3.0
189          */
190         private IResourceChangeListener fResourceChangeListener;
191         private boolean fIninitalProcessDone= false;
192         
193         /**
194          * Creates a new reconciler.
195          */
196         public JavaReconciler(ITextEditor editor, JavaCompositeReconcilingStrategy strategy, boolean isIncremental) {
197                 super(strategy, isIncremental);
198                 fTextEditor= editor;
199                 
200                 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=63898
201                 // when re-using editors, a new reconciler is set up by the source viewer
202                 // and the old one uninstalled. However, the old reconciler may still be
203                 // running. 
204                 // To avoid having to reconcilers calling CompilationUnitEditor.reconciled,
205                 // we synchronized on a lock object provided by the editor.
206                 // The critical section is really the entire run() method of the reconciler
207                 // thread, but synchronizing process() only will keep JavaReconcilingStrategy
208                 // from running concurrently on the same editor.
209                 // TODO remove once we have ensured that there is only one reconciler per editor. 
210                 if (editor instanceof PHPUnitEditor)
211                         fMutex= ((PHPUnitEditor) editor).getReconcilerLock();
212                 else
213                         fMutex= new Object(); // Null Object
214         }
215         
216         /*
217          * @see org.eclipse.jface.text.reconciler.IReconciler#install(org.eclipse.jface.text.ITextViewer)
218          */
219         public void install(ITextViewer textViewer) {
220                 super.install(textViewer);
221                 
222                 fPartListener= new PartListener();
223                 IWorkbenchPartSite site= fTextEditor.getSite();
224                 IWorkbenchWindow window= site.getWorkbenchWindow();
225                 window.getPartService().addPartListener(fPartListener);
226                 
227                 fActivationListener= new ActivationListener(textViewer.getTextWidget());
228                 Shell shell= window.getShell();
229                 shell.addShellListener(fActivationListener);
230                 
231                 fJavaElementChangedListener= new ElementChangedListener();
232                 JavaCore.addElementChangedListener(fJavaElementChangedListener);
233                 
234                 fResourceChangeListener= new ResourceChangeListener();
235                 IWorkspace workspace= PHPeclipsePlugin.getWorkspace();
236                 workspace.addResourceChangeListener(fResourceChangeListener);
237         }
238
239         /*
240          * @see org.eclipse.jface.text.reconciler.IReconciler#uninstall()
241          */
242         public void uninstall() {
243                 
244                 IWorkbenchPartSite site= fTextEditor.getSite();
245                 IWorkbenchWindow window= site.getWorkbenchWindow();
246                 window.getPartService().removePartListener(fPartListener);
247                 fPartListener= null;
248                 
249                 Shell shell= window.getShell();
250                 if (shell != null && !shell.isDisposed())
251                         shell.removeShellListener(fActivationListener);
252                 fActivationListener= null;
253                 
254                 JavaCore.removeElementChangedListener(fJavaElementChangedListener);
255                 fJavaElementChangedListener= null;
256                 
257                 IWorkspace workspace= PHPeclipsePlugin.getWorkspace();
258                 workspace.removeResourceChangeListener(fResourceChangeListener);
259                 fResourceChangeListener= null;
260                 
261                 super.uninstall();
262         }
263         
264         /*
265          * @see org.eclipse.jface.text.reconciler.AbstractReconciler#forceReconciling()
266          */
267         protected void forceReconciling() {
268                 if (!fIninitalProcessDone)
269                         return;
270                 
271                 super.forceReconciling();
272         JavaCompositeReconcilingStrategy strategy= (JavaCompositeReconcilingStrategy) getReconcilingStrategy(IDocument.DEFAULT_CONTENT_TYPE);
273                 strategy.notifyListeners(false);
274         }
275     
276         /*
277          * @see org.eclipse.jface.text.reconciler.AbstractReconciler#aboutToReconcile()
278          * @since 3.0
279          */
280         protected void aboutToBeReconciled() {
281                 JavaCompositeReconcilingStrategy strategy= (JavaCompositeReconcilingStrategy) getReconcilingStrategy(IDocument.DEFAULT_CONTENT_TYPE);
282                 strategy.aboutToBeReconciled();
283         }
284          
285         /*
286          * @see org.eclipse.jface.text.reconciler.AbstractReconciler#reconcilerReset()
287          */
288         protected void reconcilerReset() {
289                 super.reconcilerReset();
290         JavaCompositeReconcilingStrategy strategy= (JavaCompositeReconcilingStrategy) getReconcilingStrategy(IDocument.DEFAULT_CONTENT_TYPE);
291                 strategy.notifyListeners(true);
292         }
293         
294         /*
295          * @see org.eclipse.jface.text.reconciler.MonoReconciler#initialProcess()
296          */
297         protected void initialProcess() {
298                 // TODO remove once we have ensured that there is only one reconciler per editor. 
299                 synchronized (fMutex) {
300                         super.initialProcess();
301                 }
302                 fIninitalProcessDone= true;
303         }
304         
305         /*
306          * @see org.eclipse.jface.text.reconciler.MonoReconciler#process(org.eclipse.jface.text.reconciler.DirtyRegion)
307          */
308         protected void process(DirtyRegion dirtyRegion) {
309                 // TODO remove once we have ensured that there is only one reconciler per editor. 
310                 synchronized (fMutex) { 
311                         super.process(dirtyRegion);
312                 }
313         }
314         
315         /**
316          * Tells whether the Java Model has changed or not.
317          * 
318          * @return <code>true</code> iff the Java Model has changed
319          * @since 3.0
320          */
321         private synchronized boolean hasJavaModelChanged() {
322                 return fHasJavaModelChanged;
323         }
324         
325         /**
326          * Sets whether the Java Model has changed or not.
327          * 
328          * @param state <code>true</code> iff the java model has changed
329          * @since 3.0
330          */
331         private synchronized void setJavaModelChanged(boolean state) {
332                 fHasJavaModelChanged= state;
333         }
334 }
335 ///**
336 // * A reconciler that is also activated on editor activation.
337 // */
338 //public class JavaReconciler extends MonoReconciler {
339 //      
340 //      /**
341 //       * Internal part listener for activating the reconciler.
342 //       */
343 //      class PartListener implements IPartListener {
344 //              
345 //              /*
346 //               * @see IPartListener#partActivated(IWorkbenchPart)
347 //               */
348 //              public void partActivated(IWorkbenchPart part) {
349 //                      if (part == fTextEditor)
350 //                              JavaReconciler.this.forceReconciling();
351 //              }
352 //
353 //              /*
354 //               * @see IPartListener#partBroughtToTop(IWorkbenchPart)
355 //               */
356 //              public void partBroughtToTop(IWorkbenchPart part) {
357 //              }
358 //
359 //              /*
360 //               * @see IPartListener#partClosed(IWorkbenchPart)
361 //               */
362 //              public void partClosed(IWorkbenchPart part) {
363 //              }
364 //
365 //              /*
366 //               * @see IPartListener#partDeactivated(IWorkbenchPart)
367 //               */
368 //              public void partDeactivated(IWorkbenchPart part) {
369 //              }
370 //
371 //              /*
372 //               * @see IPartListener#partOpened(IWorkbenchPart)
373 //               */
374 //              public void partOpened(IWorkbenchPart part) {
375 //              }
376 //      };
377 //      
378 //      
379 //      /** The reconciler's editor */
380 //      private ITextEditor fTextEditor;
381 //      /** The part listener */
382 //      private IPartListener fPartListener;
383 //      
384 //      
385 //      /**
386 //       * Creates a new reconciler.
387 //       */
388 //      public JavaReconciler(ITextEditor editor, IReconcilingStrategy strategy, boolean isIncremental) {
389 //              super(strategy, isIncremental);
390 //              fTextEditor= editor;
391 //      }
392 //      
393 //      /*
394 //       * @see IReconciler#install(ITextViewer)
395 //       */
396 //      public void install(ITextViewer textViewer) {
397 //              super.install(textViewer);
398 //              
399 //              fPartListener= new PartListener();
400 //              IWorkbenchPartSite site= fTextEditor.getSite();
401 //              IWorkbenchWindow window= site.getWorkbenchWindow();
402 //              window.getPartService().addPartListener(fPartListener);
403 //      }
404 //
405 //      /*
406 //       * @see IReconciler#uninstall()
407 //       */
408 //      public void uninstall() {
409 //              
410 //              IWorkbenchPartSite site= fTextEditor.getSite();
411 //              IWorkbenchWindow window= site.getWorkbenchWindow();
412 //              window.getPartService().removePartListener(fPartListener);
413 //              fPartListener= null;
414 //              
415 //              super.uninstall();
416 //      }
417 //      
418 //    /*
419 //       * @see AbstractReconciler#forceReconciling()
420 //       */
421 //      protected void forceReconciling() {
422 //              super.forceReconciling();
423 //        IReconcilingStrategy strategy= getReconcilingStrategy(IDocument.DEFAULT_CONTENT_TYPE);
424 //        if (strategy instanceof JavaReconcilingStrategy) {
425 //                      JavaReconcilingStrategy java= (JavaReconcilingStrategy) strategy;
426 //                      java.notifyParticipants(false);
427 //              }
428 //      }
429 //    
430 //      /*
431 //       * @see AbstractReconciler#reconcilerReset()
432 //       */
433 //      protected void reconcilerReset() {
434 //              super.reconcilerReset();
435 //        IReconcilingStrategy strategy= getReconcilingStrategy(IDocument.DEFAULT_CONTENT_TYPE);
436 //        if (strategy instanceof JavaReconcilingStrategy) {
437 //                      JavaReconcilingStrategy java= (JavaReconcilingStrategy) strategy;
438 //                      java.notifyParticipants(true);
439 //              }
440 //      }
441 //}