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