upload tizen1.0 source
[sdk/ide/product.git] / org.eclipse.cdt.ui / src / org / eclipse / cdt / internal / ui / text / CReconciler.java
1 /*******************************************************************************
2  * Copyright (c) 2006, 2011 Wind River Systems, Inc. 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  *     Anton Leherbauer (Wind River Systems) - initial API and implementation
10  *     Markus Schorn (Wind River Systems)
11  *******************************************************************************/
12 package org.eclipse.cdt.internal.ui.text;
13
14 import org.eclipse.core.resources.IProject;
15 import org.eclipse.core.runtime.Assert;
16 import org.eclipse.core.runtime.CoreException;
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.ISchedulingRule;
21 import org.eclipse.core.runtime.jobs.Job;
22 import org.eclipse.jface.text.IDocument;
23 import org.eclipse.jface.text.ITextViewer;
24 import org.eclipse.jface.text.reconciler.DirtyRegion;
25 import org.eclipse.jface.text.reconciler.MonoReconciler;
26 import org.eclipse.swt.events.ShellAdapter;
27 import org.eclipse.swt.events.ShellEvent;
28 import org.eclipse.swt.events.ShellListener;
29 import org.eclipse.swt.widgets.Control;
30 import org.eclipse.swt.widgets.Shell;
31 import org.eclipse.ui.IEditorInput;
32 import org.eclipse.ui.IPartListener2;
33 import org.eclipse.ui.IWorkbenchPartReference;
34 import org.eclipse.ui.IWorkbenchPartSite;
35 import org.eclipse.ui.IWorkbenchWindow;
36 import org.eclipse.ui.texteditor.ITextEditor;
37
38 import org.eclipse.cdt.core.CCorePlugin;
39 import org.eclipse.cdt.core.index.IIndexChangeEvent;
40 import org.eclipse.cdt.core.index.IIndexChangeListener;
41 import org.eclipse.cdt.core.index.IIndexerStateEvent;
42 import org.eclipse.cdt.core.index.IIndexerStateListener;
43 import org.eclipse.cdt.core.model.CoreModel;
44 import org.eclipse.cdt.core.model.ElementChangedEvent;
45 import org.eclipse.cdt.core.model.ICElement;
46 import org.eclipse.cdt.core.model.ICElementDelta;
47 import org.eclipse.cdt.core.model.ICProject;
48 import org.eclipse.cdt.core.model.IElementChangedListener;
49 import org.eclipse.cdt.core.model.ITranslationUnit;
50 import org.eclipse.cdt.core.model.IWorkingCopy;
51 import org.eclipse.cdt.ui.CUIPlugin;
52 import org.eclipse.cdt.ui.IWorkingCopyManager;
53
54 /**
55  * A single strategy C reconciler.
56  * 
57  * @since 4.0
58  */
59 public class CReconciler extends MonoReconciler {
60
61         static class SingletonJob extends Job implements ISchedulingRule {
62                 private Runnable fCode;
63
64                 SingletonJob(String name, Runnable code) {
65                         super(name);
66                         fCode= code;
67                         setPriority(Job.SHORT);
68                         setRule(this);
69                         setUser(false);
70                         setSystem(true);
71                 }
72
73                 /*
74                  * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
75                  */
76                 @Override
77                 protected IStatus run(IProgressMonitor monitor) {
78                         if (!monitor.isCanceled()) {
79                                 fCode.run();
80                         }
81                         return Status.OK_STATUS;
82                 }
83
84                 /*
85                  * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
86                  */
87                 public boolean contains(ISchedulingRule rule) {
88                         return rule == this;
89                 }
90
91                 /*
92                  * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
93                  */
94                 public boolean isConflicting(ISchedulingRule rule) {
95                         return rule == this;
96                 }
97                 
98         }
99
100         /**
101          * Internal part listener for activating the reconciler.
102          */
103         private class PartListener implements IPartListener2 {
104                 /*
105                  * @see org.eclipse.ui.IPartListener2#partActivated(org.eclipse.ui.IWorkbenchPartReference)
106                  */
107                 public void partActivated(IWorkbenchPartReference partRef) {
108                 }
109                 /*
110                  * @see org.eclipse.ui.IPartListener2#partBroughtToTop(org.eclipse.ui.IWorkbenchPartReference)
111                  */
112                 public void partBroughtToTop(IWorkbenchPartReference partRef) {
113                 }
114                 /*
115                  * @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference)
116                  */
117                 public void partClosed(IWorkbenchPartReference partRef) {
118                 }
119                 /*
120                  * @see org.eclipse.ui.IPartListener2#partDeactivated(org.eclipse.ui.IWorkbenchPartReference)
121                  */
122                 public void partDeactivated(IWorkbenchPartReference partRef) {
123                 }
124                 /*
125                  * @see org.eclipse.ui.IPartListener2#partHidden(org.eclipse.ui.IWorkbenchPartReference)
126                  */
127                 public void partHidden(IWorkbenchPartReference partRef) {
128                         if (partRef.getPart(false) == fTextEditor) {
129                                 setEditorActive(false);
130                         }
131                 }
132                 /*
133                  * @see org.eclipse.ui.IPartListener2#partInputChanged(org.eclipse.ui.IWorkbenchPartReference)
134                  */
135                 public void partInputChanged(IWorkbenchPartReference partRef) {
136                 }
137                 /*
138                  * @see org.eclipse.ui.IPartListener2#partOpened(org.eclipse.ui.IWorkbenchPartReference)
139                  */
140                 public void partOpened(IWorkbenchPartReference partRef) {
141                 }
142                 /*
143                  * @see org.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui.IWorkbenchPartReference)
144                  */
145                 public void partVisible(IWorkbenchPartReference partRef) {
146                         if (partRef.getPart(false) == fTextEditor) {
147                                 CReconciler.this.scheduleReconciling();
148                                 setEditorActive(true);
149                         }
150                 }
151         }
152
153         /**
154          * Internal Shell activation listener for activating the reconciler.
155          */
156         private class ActivationListener extends ShellAdapter {
157
158                 private Control fControl;
159
160                 public ActivationListener(Control control) {
161                         Assert.isNotNull(control);
162                         fControl= control;
163                 }
164
165                 /*
166                  * @see org.eclipse.swt.events.ShellListener#shellActivated(org.eclipse.swt.events.ShellEvent)
167                  */
168                 @Override
169                 public void shellActivated(ShellEvent e) {
170                         if (!fControl.isDisposed() && fControl.isVisible()) {
171                                 if (hasCModelChanged())
172                                         CReconciler.this.scheduleReconciling();
173                                 setEditorActive(true);
174                         }
175                 }
176
177                 /*
178                  * @see org.eclipse.swt.events.ShellListener#shellDeactivated(org.eclipse.swt.events.ShellEvent)
179                  */
180                 @Override
181                 public void shellDeactivated(ShellEvent e) {
182                         if (!fControl.isDisposed() && fControl.getShell() == e.getSource()) {
183                                 setEditorActive(false);
184                         }
185                 }
186         }
187
188         /**
189          * Internal C element changed listener
190          */
191         private class ElementChangedListener implements IElementChangedListener {
192                 /*
193                  * @see org.eclipse.cdt.core.model.IElementChangedListener#elementChanged(org.eclipse.cdt.core.model.ElementChangedEvent)
194                  */
195                 public void elementChanged(ElementChangedEvent event) {
196                         if (event.getType() == ElementChangedEvent.POST_CHANGE) {
197                                 if (isRelevantDelta(event.getDelta())) {
198                                         if (!fIsReconciling && isEditorActive() && fInitialProcessDone) {
199                                                 CReconciler.this.scheduleReconciling();
200                                         } else {
201                                                 setCModelChanged(true);
202                                         }
203                                 }
204                         }
205                 }
206
207                 private boolean isRelevantDelta(ICElementDelta delta) {
208                         final int flags = delta.getFlags();
209                         if ((flags & ICElementDelta.F_CONTENT) != 0) {
210                                 if (!fIsReconciling && isRelevantElement(delta.getElement())) {
211                                         // mark model changed, but don't update immediately
212                                         fIndexerListener.ignoreChanges(false);
213                                         setCModelChanged(true);
214                                 } else if (delta.getElement() instanceof ITranslationUnit) {
215                                         fIndexerListener.ignoreChanges(true);
216                                 }
217                         }
218                         if ((flags & (
219                                         ICElementDelta.F_CHANGED_PATHENTRY_INCLUDE | 
220                                         ICElementDelta.F_CHANGED_PATHENTRY_MACRO
221                                         )) != 0) {
222                                 if (isRelevantProject(delta.getElement().getCProject())) {
223                                         return true;
224                                 }
225                         }
226                         if ((flags & ICElementDelta.F_CHILDREN) != 0) {
227                                 ICElementDelta[] childDeltas= delta.getChangedChildren();
228                                 for (int i = 0; i < childDeltas.length; i++) {
229                                         if (isRelevantDelta(childDeltas[i])) {
230                                                 return true;
231                                         }
232                                 }
233                         }
234                         return false;
235                 }
236         }
237
238         private class IndexerListener implements IIndexerStateListener, IIndexChangeListener {
239                 private boolean fIndexChanged;
240                 private boolean fIgnoreChanges;
241
242                 /*
243                  * @see org.eclipse.cdt.core.index.IIndexerStateListener#indexChanged(org.eclipse.cdt.core.index.IIndexerStateEvent)
244                  */
245                 public void indexChanged(IIndexerStateEvent event) {
246                         if (event.indexerIsIdle()) {
247                                 if (fIndexChanged || hasCModelChanged()) {
248                                         fIndexChanged= false;
249                                         if (!fIsReconciling && isEditorActive() && fInitialProcessDone) {
250                                                 CReconciler.this.scheduleReconciling();
251                                         } else {
252                                                 setCModelChanged(true);
253                                         }
254                                 }
255                                 fIgnoreChanges= false;
256                         }
257                 }
258
259                 public void ignoreChanges(boolean ignore) {
260                         fIgnoreChanges= ignore;
261                 }
262
263                 /*
264                  * @see org.eclipse.cdt.core.index.IIndexChangeListener#indexChanged(org.eclipse.cdt.core.index.IIndexChangeEvent)
265                  */
266                 public void indexChanged(IIndexChangeEvent event) {
267                         if (!fIndexChanged && isRelevantProject(event.getAffectedProject())) {
268                                 if (!fIgnoreChanges || event.isCleared() || event.isReloaded() || event.hasNewFile()) {
269                                         fIndexChanged= true;
270                                 }
271                         }
272                 }
273                 
274         }
275
276         /** The reconciler's editor */
277         private ITextEditor fTextEditor;
278         /** The part listener */
279         private IPartListener2 fPartListener;
280         /** The shell listener */
281         private ShellListener fActivationListener;
282         /** The C element changed listener.  */
283         private IElementChangedListener fCElementChangedListener;
284         /** The indexer listener */
285         private IndexerListener fIndexerListener; 
286         /** Tells whether the C model might have changed. */
287         private volatile boolean fHasCModelChanged= false;
288         /** Tells whether this reconciler's editor is active. */
289         private volatile boolean fIsEditorActive= true;
290         /** Tells whether a reconcile is in progress. */
291         private volatile boolean fIsReconciling= false;
292         
293         private boolean fInitialProcessDone= false;
294         private Job fTriggerReconcilerJob;
295
296         /**
297          * Create a reconciler for the given editor and strategy.
298          * 
299          * @param editor the text editor
300          * @param strategy  the C reconciling strategy
301          */
302         public CReconciler(ITextEditor editor, CCompositeReconcilingStrategy strategy) {
303                 super(strategy, false);
304                 fTextEditor= editor;
305         }
306
307         /*
308          * @see org.eclipse.jface.text.reconciler.IReconciler#install(org.eclipse.jface.text.ITextViewer)
309          */
310         @Override
311         public void install(ITextViewer textViewer) {
312                 super.install(textViewer);
313                 
314                 fPartListener= new PartListener();
315                 IWorkbenchPartSite site= fTextEditor.getSite();
316                 IWorkbenchWindow window= site.getWorkbenchWindow();
317                 window.getPartService().addPartListener(fPartListener);
318
319                 fActivationListener= new ActivationListener(textViewer.getTextWidget());
320                 Shell shell= window.getShell();
321                 shell.addShellListener(fActivationListener);
322
323                 fCElementChangedListener= new ElementChangedListener();
324                 CoreModel.getDefault().addElementChangedListener(fCElementChangedListener);
325                 
326                 fIndexerListener= new IndexerListener();
327                 CCorePlugin.getIndexManager().addIndexerStateListener(fIndexerListener);
328                 CCorePlugin.getIndexManager().addIndexChangeListener(fIndexerListener);
329                 
330                 fTriggerReconcilerJob= new SingletonJob("Trigger Reconciler", new Runnable() { //$NON-NLS-1$
331                         public void run() {
332                                 forceReconciling();
333                         }});
334         }
335
336         /*
337          * @see org.eclipse.jface.text.reconciler.IReconciler#uninstall()
338          */
339         @Override
340         public void uninstall() {
341                 fTriggerReconcilerJob.cancel();
342                 
343                 IWorkbenchPartSite site= fTextEditor.getSite();
344                 IWorkbenchWindow window= site.getWorkbenchWindow();
345                 window.getPartService().removePartListener(fPartListener);
346                 fPartListener= null;
347
348                 Shell shell= window.getShell();
349                 if (shell != null && !shell.isDisposed())
350                         shell.removeShellListener(fActivationListener);
351                 fActivationListener= null;
352
353                 CoreModel.getDefault().removeElementChangedListener(fCElementChangedListener);
354                 fCElementChangedListener= null;
355
356                 CCorePlugin.getIndexManager().removeIndexerStateListener(fIndexerListener);
357                 CCorePlugin.getIndexManager().removeIndexChangeListener(fIndexerListener);
358                 fIndexerListener= null;
359                 super.uninstall();
360         }
361
362         protected void scheduleReconciling() {
363                 if (!fInitialProcessDone)
364                         return;
365                 if (fTriggerReconcilerJob.cancel()) {
366                         fTriggerReconcilerJob.schedule(50);
367                 }
368         }
369
370         /*
371          * @see org.eclipse.jface.text.reconciler.AbstractReconciler#forceReconciling()
372          */
373         @Override
374         protected void forceReconciling() {
375                 if (!fInitialProcessDone)
376                         return;
377                 super.forceReconciling();
378         }
379
380         /*
381          * @see org.eclipse.jface.text.reconciler.AbstractReconciler#aboutToBeReconciled()
382          */
383         @Override
384         protected void aboutToBeReconciled() {
385                 CCompositeReconcilingStrategy strategy= (CCompositeReconcilingStrategy)getReconcilingStrategy(IDocument.DEFAULT_CONTENT_TYPE);
386                 strategy.aboutToBeReconciled();
387         }
388
389         /*
390          * @see org.eclipse.jface.text.reconciler.MonoReconciler#initialProcess()
391          */
392         @Override
393         protected void initialProcess() {
394                 super.initialProcess();
395                 fInitialProcessDone= true;
396                 if (!fIsReconciling && isEditorActive() && hasCModelChanged()) {
397                         CReconciler.this.scheduleReconciling();
398                 } 
399         }
400
401         /*
402          * @see org.eclipse.jface.text.reconciler.MonoReconciler#process(org.eclipse.jface.text.reconciler.DirtyRegion)
403          */
404         @Override
405         protected void process(DirtyRegion dirtyRegion) {
406                 fIsReconciling= true;
407                 setCModelChanged(false);
408                 super.process(dirtyRegion);
409                 fIsReconciling= false;
410         }
411
412         /**
413          * Tells whether the C Model has changed or not.
414          *
415          * @return <code>true</code> iff the C Model has changed
416          */
417         private synchronized boolean hasCModelChanged() {
418                 return fHasCModelChanged;
419         }
420
421         /**
422          * Sets whether the C Model has changed or not.
423          *
424          * @param state <code>true</code> iff the C model has changed
425          */
426         private synchronized void setCModelChanged(boolean state) {
427                 fHasCModelChanged= state;
428         }
429         
430         /**
431          * Tells whether this reconciler's editor is active.
432          *
433          * @return <code>true</code> iff the editor is active
434          */
435         private synchronized boolean isEditorActive() {
436                 return fIsEditorActive;
437         }
438
439         /**
440          * Sets whether this reconciler's editor is active.
441          *
442          * @param state <code>true</code> iff the editor is active
443          */
444         private synchronized void setEditorActive(boolean active) {
445                 fIsEditorActive= active;
446                 if (!active) {
447                         fTriggerReconcilerJob.cancel();
448                 }
449         }
450
451         public boolean isRelevantElement(ICElement element) {
452                 if (!fInitialProcessDone) {
453                         return false;
454                 }
455                 if (element instanceof IWorkingCopy) {
456                         return false;
457                 }
458                 if (element instanceof ITranslationUnit) {
459                         IEditorInput input= fTextEditor.getEditorInput();
460                         IWorkingCopyManager manager= CUIPlugin.getDefault().getWorkingCopyManager();                            
461                         IWorkingCopy copy= manager.getWorkingCopy(input);
462                         if (copy == null || copy.getOriginalElement().equals(element)) {
463                                 return false;
464                         }
465                         return isRelevantProject(copy.getCProject());
466                 }
467                 return false;
468         }
469
470
471         private boolean isRelevantProject(ICProject affectedProject) {
472                 if (affectedProject == null) {
473                         return false;
474                 }
475                 IEditorInput input= fTextEditor.getEditorInput();
476                 IWorkingCopyManager manager= CUIPlugin.getDefault().getWorkingCopyManager();                            
477                 IWorkingCopy copy= manager.getWorkingCopy(input);
478                 if (copy == null) {
479                         return false;
480                 }
481                 if (copy.getCProject().equals(affectedProject)) {
482                         return true;
483                 }
484                 IProject project= copy.getCProject().getProject();
485                 if (project == null) {
486                         return false;
487                 }
488                 try {
489                         IProject[] referencedProjects= project.getReferencedProjects();
490                         for (int i= 0; i < referencedProjects.length; i++) {
491                                 project= referencedProjects[i];
492                                 if (project.equals(affectedProject.getProject())) {
493                                         return true;
494                                 }
495                         }
496                 } catch (CoreException exc) {
497                 }
498                 return false;
499         }
500
501 }