KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > ui > synchronize > SynchronizeModelUpdateHandler


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 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 package org.eclipse.team.internal.ui.synchronize;
12
13 import com.ibm.icu.text.DateFormat;
14 import com.ibm.icu.text.SimpleDateFormat;
15 import java.util.Collections JavaDoc;
16 import java.util.Date JavaDoc;
17 import java.util.HashMap JavaDoc;
18 import java.util.HashSet JavaDoc;
19 import java.util.Iterator JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.Set JavaDoc;
22
23 import org.eclipse.core.resources.*;
24 import org.eclipse.core.runtime.*;
25 import org.eclipse.jface.util.IPropertyChangeListener;
26 import org.eclipse.jface.util.PropertyChangeEvent;
27 import org.eclipse.jface.viewers.AbstractTreeViewer;
28 import org.eclipse.jface.viewers.StructuredViewer;
29 import org.eclipse.swt.custom.BusyIndicator;
30 import org.eclipse.swt.widgets.Control;
31 import org.eclipse.team.core.ITeamStatus;
32 import org.eclipse.team.core.TeamException;
33 import org.eclipse.team.core.synchronize.*;
34 import org.eclipse.team.internal.core.BackgroundEventHandler;
35 import org.eclipse.team.internal.ui.*;
36 import org.eclipse.team.ui.synchronize.ISynchronizeModelElement;
37
38 /**
39  * Handler that serializes the updating of a synchronize model provider.
40  * All modifications to the synchronize model are performed in this
41  * handler's thread.
42  */

43 public class SynchronizeModelUpdateHandler extends BackgroundEventHandler implements IResourceChangeListener, ISyncInfoSetChangeListener {
44     
45     private static final boolean DEBUG = Policy.DEBUG_SYNC_MODELS;
46     
47     private static final IWorkspaceRoot ROOT = ResourcesPlugin.getWorkspace().getRoot();
48     
49     // Event that indicates that the markers for a set of elements has changed
50
private static final int MARKERS_CHANGED = 1;
51     private static final int BUSY_STATE_CHANGED = 2;
52     private static final int RESET = 3;
53     private static final int SYNC_INFO_SET_CHANGED = 4;
54     
55     private AbstractSynchronizeModelProvider provider;
56     
57     private Set JavaDoc pendingLabelUpdates = Collections.synchronizedSet(new HashSet JavaDoc());
58     
59     // Flag to indicate the need for an early dispath in order to show
60
// busy for elements involved in an operation
61
private boolean dispatchEarly = false;
62     
63     private static final int EARLY_DISPATCH_INCREMENT = 100;
64     
65     /**
66      * Custom event for posting marker changes
67      */

68     class MarkerChangeEvent extends Event {
69         private final ISynchronizeModelElement[] elements;
70         public MarkerChangeEvent(ISynchronizeModelElement[] elements) {
71             super(MARKERS_CHANGED);
72             this.elements = elements;
73         }
74         public ISynchronizeModelElement[] getElements() {
75             return elements;
76         }
77     }
78     
79     /**
80      * Custom event for posting busy state changes
81      */

82     class BusyStateChangeEvent extends Event {
83         
84         private final ISynchronizeModelElement element;
85         private final boolean isBusy;
86         public BusyStateChangeEvent(ISynchronizeModelElement element, boolean isBusy) {
87             super(BUSY_STATE_CHANGED);
88             this.element = element;
89             this.isBusy = isBusy;
90         }
91         public ISynchronizeModelElement getElement() {
92             return element;
93         }
94         public boolean isBusy() {
95             return isBusy;
96         }
97     }
98     
99     /**
100      * Custom event for posting sync info set changes
101      */

102     class SyncInfoSetChangeEvent extends Event {
103         private final ISyncInfoSetChangeEvent event;
104         public SyncInfoSetChangeEvent(ISyncInfoSetChangeEvent event) {
105             super(SYNC_INFO_SET_CHANGED);
106             this.event = event;
107         }
108         public ISyncInfoSetChangeEvent getEvent() {
109             return event;
110         }
111     }
112     
113     private IPropertyChangeListener listener = new IPropertyChangeListener() {
114         public void propertyChange(final PropertyChangeEvent event) {
115             if (event.getProperty() == ISynchronizeModelElement.BUSY_PROPERTY) {
116                 Object JavaDoc source = event.getSource();
117                 if (source instanceof ISynchronizeModelElement)
118                     updateBusyState((ISynchronizeModelElement)source, ((Boolean JavaDoc)event.getNewValue()).booleanValue());
119             }
120         }
121     };
122
123     private boolean performingBackgroundUpdate;
124     
125     /*
126      * Map used to keep track of additions so they can be added in batch at the end of the update
127      */

128     private Map JavaDoc additionsMap;
129     
130     /**
131      * Create the marker update handler.
132      */

133     public SynchronizeModelUpdateHandler(AbstractSynchronizeModelProvider provider) {
134         super(TeamUIMessages.SynchronizeModelProvider_0, TeamUIMessages.SynchronizeModelUpdateHandler_0); //
135
this.provider = provider;
136         ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
137         provider.getSyncInfoSet().addSyncSetChangedListener(this);
138     }
139     
140     /**
141      * Return the marker types that are of interest to this handler.
142      * @return the marker types that are of interest to this handler
143      */

144     protected String JavaDoc[] getMarkerTypes() {
145         return new String JavaDoc[] {IMarker.PROBLEM};
146     }
147     
148     /**
149      * Return the <code>AbstractTreeViewer</code> associated with this
150      * provider or <code>null</code> if the viewer is not of the proper type.
151      * @return the structured viewer that is displaying the model managed by this provider
152      */

153     public StructuredViewer getViewer() {
154         return provider.getViewer();
155     }
156     
157     /* (non-Javadoc)
158      * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
159      */

160     public void resourceChanged(final IResourceChangeEvent event) {
161             String JavaDoc[] markerTypes = getMarkerTypes();
162             Set JavaDoc handledResources = new HashSet JavaDoc();
163             Set JavaDoc changes = new HashSet JavaDoc();
164             
165             // Accumulate all distinct resources that have had problem marker
166
// changes
167
for (int idx = 0; idx < markerTypes.length; idx++) {
168                 IMarkerDelta[] markerDeltas = event.findMarkerDeltas(markerTypes[idx], true);
169                     for (int i = 0; i < markerDeltas.length; i++) {
170                         IMarkerDelta delta = markerDeltas[i];
171                         IResource resource = delta.getResource();
172                         if (!handledResources.contains(resource)) {
173                             handledResources.add(resource);
174                             ISynchronizeModelElement[] elements = provider.getClosestExistingParents(delta.getResource());
175                             if(elements != null && elements.length > 0) {
176                                 for (int j = 0; j < elements.length; j++) {
177                                     ISynchronizeModelElement element = elements[j];
178                                     changes.add(element);
179                                 }
180                             }
181                         }
182                     }
183                 }
184             
185             if (!changes.isEmpty()) {
186                 updateMarkersFor((ISynchronizeModelElement[]) changes.toArray(new ISynchronizeModelElement[changes.size()]));
187         }
188     }
189     
190     private void updateMarkersFor(ISynchronizeModelElement[] elements) {
191         queueEvent(new MarkerChangeEvent(elements), false /* not on front of queue */);
192     }
193     
194     protected void updateBusyState(ISynchronizeModelElement element, boolean isBusy) {
195         queueEvent(new BusyStateChangeEvent(element, isBusy), false /* not on front of queue */);
196     }
197
198     /* (non-Javadoc)
199      * @see org.eclipse.team.internal.core.BackgroundEventHandler#processEvent(org.eclipse.team.internal.core.BackgroundEventHandler.Event, org.eclipse.core.runtime.IProgressMonitor)
200      */

201     protected void processEvent(Event event, IProgressMonitor monitor) throws CoreException {
202         switch (event.getType()) {
203         case BackgroundEventHandler.RUNNABLE_EVENT :
204             executeRunnable(event, monitor);
205             break;
206         case MARKERS_CHANGED:
207             // Changes contains all elements that need their labels updated
208
long start = System.currentTimeMillis();
209             ISynchronizeModelElement[] elements = getChangedElements(event);
210             for (int i = 0; i < elements.length; i++) {
211                 ISynchronizeModelElement element = elements[i];
212                 propagateProblemMarkers(element);
213                 updateParentLabels(element);
214             }
215             if (DEBUG) {
216                 long time = System.currentTimeMillis() - start;
217                 DateFormat TIME_FORMAT = new SimpleDateFormat("m:ss.SSS"); //$NON-NLS-1$
218
String JavaDoc took = TIME_FORMAT.format(new Date JavaDoc(time));
219                 System.out.println(took + " for " + elements.length + " files"); //$NON-NLS-1$//$NON-NLS-2$
220
}
221             break;
222         case BUSY_STATE_CHANGED:
223             BusyStateChangeEvent e = (BusyStateChangeEvent)event;
224             queueForLabelUpdate(e.getElement());
225             if (e.isBusy()) {
226                 // indicate that we want an early dispatch to show busy elements
227
dispatchEarly = true;
228             }
229             break;
230         case RESET:
231             // Perform the reset immediately
232
pendingLabelUpdates.clear();
233             provider.reset();
234             break;
235         case SYNC_INFO_SET_CHANGED:
236             // Handle the sync change immediately
237
handleChanges(((SyncInfoSetChangeEvent)event).getEvent(), monitor);
238         default:
239             break;
240         }
241     }
242
243     private ISynchronizeModelElement[] getChangedElements(Event event) {
244         if (event.getType() == MARKERS_CHANGED) {
245             return ((MarkerChangeEvent)event).getElements();
246         }
247         return new ISynchronizeModelElement[0];
248     }
249
250     /* (non-Javadoc)
251      * @see org.eclipse.team.internal.core.BackgroundEventHandler#doDispatchEvents(org.eclipse.core.runtime.IProgressMonitor)
252      */

253     protected boolean doDispatchEvents(IProgressMonitor monitor) throws TeamException {
254         // Fire label changed
255
dispatchEarly = false;
256         if (pendingLabelUpdates.isEmpty()) {
257             return false;
258         } else {
259             Utils.asyncExec(new Runnable JavaDoc() {
260                 public void run() {
261                     firePendingLabelUpdates();
262                 }
263             }, getViewer());
264             return true;
265         }
266     }
267     
268     /**
269      * Forces the viewer to update the labels for queued elemens
270      * whose label has changed during this round of changes. This method
271      * should only be invoked in the UI thread.
272      */

273     protected void firePendingLabelUpdates() {
274         if (!Utils.canUpdateViewer(getViewer())) return;
275         try {
276             Object JavaDoc[] updates = pendingLabelUpdates.toArray(new Object JavaDoc[pendingLabelUpdates.size()]);
277             updateLabels(updates);
278         } finally {
279             pendingLabelUpdates.clear();
280         }
281     }
282     
283     /*
284      * Forces the viewer to update the labels for the given elements
285      */

286     private void updateLabels(Object JavaDoc[] elements) {
287         StructuredViewer tree = getViewer();
288         if (Utils.canUpdateViewer(tree)) {
289             tree.update(elements, null);
290         }
291     }
292     
293     /**
294      * Queue all the parent elements for a label update.
295      * @param element the element whose label and parent labels need to be updated
296      */

297     public void updateParentLabels(ISynchronizeModelElement element) {
298         queueForLabelUpdate(element);
299         while (element.getParent() != null) {
300             element = (ISynchronizeModelElement)element.getParent();
301             queueForLabelUpdate(element);
302         }
303     }
304     
305     /**
306      * Update the label of the given diff node. Diff nodes
307      * are accumulated and updated in a single call.
308      * @param diffNode the diff node to be updated
309      */

310     protected void queueForLabelUpdate(ISynchronizeModelElement diffNode) {
311         pendingLabelUpdates.add(diffNode);
312     }
313     
314     /**
315      * Calculate and propagate problem markers in the element model
316      * @param element the ssynchronize element
317      */

318     private void propagateProblemMarkers(ISynchronizeModelElement element) {
319         IResource resource = element.getResource();
320         if (resource != null) {
321             String JavaDoc property = provider.calculateProblemMarker(element);
322             // If it doesn't have a direct change, a parent might
323
boolean recalculateParentDecorations = hadProblemProperty(element, property);
324             if (recalculateParentDecorations) {
325                 ISynchronizeModelElement parent = (ISynchronizeModelElement) element.getParent();
326                 if (parent != null) {
327                     propagateProblemMarkers(parent);
328                 }
329             }
330         }
331     }
332     
333     // none -> error
334
// error -> none
335
// none -> warning
336
// warning -> none
337
// warning -> error
338
// error -> warning
339
private boolean hadProblemProperty(ISynchronizeModelElement element, String JavaDoc property) {
340         boolean hadError = element.getProperty(ISynchronizeModelElement.PROPAGATED_ERROR_MARKER_PROPERTY);
341         boolean hadWarning = element.getProperty(ISynchronizeModelElement.PROPAGATED_WARNING_MARKER_PROPERTY);
342         
343         // Force recalculation of parents of phantom resources
344
IResource resource = element.getResource();
345         if(resource != null && resource.isPhantom()) {
346             return true;
347         }
348         
349         if(hadError) {
350             if(! (property == ISynchronizeModelElement.PROPAGATED_ERROR_MARKER_PROPERTY)) {
351                 element.setPropertyToRoot(ISynchronizeModelElement.PROPAGATED_ERROR_MARKER_PROPERTY, false);
352                 if(property != null) {
353                     // error -> warning
354
element.setPropertyToRoot(property, true);
355                 }
356                 // error -> none
357
// recalculate parents
358
return true;
359             }
360             return false;
361         } else if(hadWarning) {
362             if(! (property == ISynchronizeModelElement.PROPAGATED_WARNING_MARKER_PROPERTY)) {
363                 element.setPropertyToRoot(ISynchronizeModelElement.PROPAGATED_WARNING_MARKER_PROPERTY, false);
364                 if(property != null) {
365                     // warning -> error
366
element.setPropertyToRoot(property, true);
367                     return false;
368                 }
369                 // warning -> none
370
return true;
371             }
372             return false;
373         } else {
374             if(property == ISynchronizeModelElement.PROPAGATED_ERROR_MARKER_PROPERTY) {
375                 // none -> error
376
element.setPropertyToRoot(property, true);
377                 return false;
378             } else if(property == ISynchronizeModelElement.PROPAGATED_WARNING_MARKER_PROPERTY) {
379                 // none -> warning
380
element.setPropertyToRoot(property, true);
381                 return true;
382             }
383             return false;
384         }
385     }
386
387     /*
388      * Queue an event that will reset the provider
389      */

390     private void reset() {
391         queueEvent(new ResourceEvent(ROOT, RESET, IResource.DEPTH_INFINITE), false);
392     }
393     
394     public void dispose() {
395         shutdown();
396         ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
397         provider.getSyncInfoSet().removeSyncSetChangedListener(this);
398     }
399     
400     /* (non-Javadoc)
401      * @see org.eclipse.team.internal.core.BackgroundEventHandler#getShortDispatchDelay()
402      */

403     protected long getShortDispatchDelay() {
404         if (dispatchEarly) {
405             dispatchEarly = false;
406             return EARLY_DISPATCH_INCREMENT;
407         }
408         return super.getShortDispatchDelay();
409     }
410
411     /**
412      * This method is invoked whenever a node is added to the viewer
413      * by the provider or a sub-provider. The handler adds an update
414      * listener to the node and notifies the root provider that
415      * a node was added.
416      * @param element the added element
417      * @param provider the provider that added the element
418      */

419     public void nodeAdded(ISynchronizeModelElement element, AbstractSynchronizeModelProvider provider) {
420         element.addPropertyChangeListener(listener);
421         this.provider.nodeAdded(element, provider);
422         if (DEBUG) {
423             System.out.println("Node added: " + getDebugDisplayLabel(element) + " -> " + getDebugDisplayLabel((ISynchronizeModelElement)element.getParent()) + " : " + getDebugDisplayLabel(provider)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
424
}
425     }
426
427     /**
428      * This method is invoked whenever a node is removed the viewer
429      * by the provider or a sub-provider. The handler removes any
430      * listener and notifies the root provider that
431      * a node was removed. The node removed may have children for which
432      * a nodeRemoved callback was not invoked (see modelObjectCleared).
433      * @param element the removed element
434      * @param provider the provider that added the element
435      */

436     public void nodeRemoved(ISynchronizeModelElement element, AbstractSynchronizeModelProvider provider) {
437         element.removePropertyChangeListener(listener);
438         this.provider.nodeRemoved(element, provider);
439         if (DEBUG) {
440             System.out.println("Node removed: " + getDebugDisplayLabel(element) + " -> " + getDebugDisplayLabel((ISynchronizeModelElement)element.getParent()) + " : " + getDebugDisplayLabel(provider)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
441
}
442     }
443
444     /**
445      * This method is invoked whenever a model object (i.e. node)
446      * is cleared from the model. This is similar to node removal but
447      * is deep.
448      * @param node the node that was cleared
449      */

450     public void modelObjectCleared(ISynchronizeModelElement node) {
451         node.removePropertyChangeListener(listener);
452         this.provider.modelObjectCleared(node);
453         if (DEBUG) {
454             System.out.println("Node cleared: " + getDebugDisplayLabel(node)); //$NON-NLS-1$
455
}
456     }
457     
458     private String JavaDoc getDebugDisplayLabel(ISynchronizeModelElement node) {
459         if (node == null) {
460             return "ROOT"; //$NON-NLS-1$
461
}
462         if (node.getResource() != null) {
463             return node.getResource().getFullPath().toString();
464         }
465         return node.getName();
466     }
467
468     private String JavaDoc getDebugDisplayLabel(AbstractSynchronizeModelProvider provider2) {
469         return provider2.toString();
470     }
471     
472     /* (non-Javadoc)
473      * @see org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener#syncInfoSetReset(org.eclipse.team.core.synchronize.SyncInfoSet, org.eclipse.core.runtime.IProgressMonitor)
474      */

475     public void syncInfoSetReset(SyncInfoSet set, IProgressMonitor monitor) {
476         if(provider.isDisposed()) {
477             set.removeSyncSetChangedListener(this);
478         } else {
479             reset();
480         }
481     }
482
483     /* (non-Javadoc)
484      * @see org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener#syncInfoChanged(org.eclipse.team.core.synchronize.ISyncInfoSetChangeEvent, org.eclipse.core.runtime.IProgressMonitor)
485      */

486     public void syncInfoChanged(final ISyncInfoSetChangeEvent event, IProgressMonitor monitor) {
487         if (! (event instanceof ISyncInfoTreeChangeEvent)) {
488             reset();
489         } else {
490             queueEvent(new SyncInfoSetChangeEvent(event), false);
491         }
492     }
493
494     /*
495      * Handle the sync info set change event in the UI thread.
496      */

497     private void handleChanges(final ISyncInfoSetChangeEvent event, final IProgressMonitor monitor) {
498         runViewUpdate(new Runnable JavaDoc() {
499             public void run() {
500                 provider.handleChanges((ISyncInfoTreeChangeEvent)event, monitor);
501                 firePendingLabelUpdates();
502             }
503         }, true /* preserve expansion */);
504     }
505
506     /* (non-Javadoc)
507      * @see org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener#syncInfoSetErrors(org.eclipse.team.core.synchronize.SyncInfoSet, org.eclipse.team.core.ITeamStatus[], org.eclipse.core.runtime.IProgressMonitor)
508      */

509     public void syncInfoSetErrors(SyncInfoSet set, ITeamStatus[] errors, IProgressMonitor monitor) {
510         // When errors occur we currently don't process them. It may be possible to decorate
511
// elements in the model with errors, but currently we prefer to let ignore and except
512
// another listener to display them.
513
}
514     
515     public ISynchronizeModelProvider getProvider() {
516         return provider;
517     }
518
519     public void connect(IProgressMonitor monitor) {
520         getProvider().getSyncInfoSet().connect(this, monitor);
521     }
522     
523     public void runViewUpdate(final Runnable JavaDoc runnable, final boolean preserveExpansion) {
524         if (Utils.canUpdateViewer(getViewer()) || isPerformingBackgroundUpdate()) {
525             internalRunViewUpdate(runnable, preserveExpansion);
526         } else {
527             if (Thread.currentThread() != getEventHandlerJob().getThread()) {
528                 // Run view update should only be called from the UI thread or
529
// the update handler thread.
530
// We will log the problem for now and make it an assert later
531
TeamUIPlugin.log(IStatus.WARNING, "View update invoked from invalid thread", new TeamException("View update invoked from invalid thread")); //$NON-NLS-1$ //$NON-NLS-2$
532
}
533             final Control ctrl = getViewer().getControl();
534             if (ctrl != null && !ctrl.isDisposed()) {
535                 ctrl.getDisplay().syncExec(new Runnable JavaDoc() {
536                     public void run() {
537                         if (!ctrl.isDisposed()) {
538                             BusyIndicator.showWhile(ctrl.getDisplay(), new Runnable JavaDoc() {
539                                 public void run() {
540                                     internalRunViewUpdate(runnable, preserveExpansion);
541                                 }
542                             });
543                         }
544                     }
545                 });
546             }
547         }
548     }
549     
550     /*
551      * Return whether the event handler is performing a background view update.
552      * In other words, a client has invoked <code>performUpdate</code>.
553      */

554     public boolean isPerformingBackgroundUpdate() {
555         return Thread.currentThread() == getEventHandlerJob().getThread() && performingBackgroundUpdate;
556     }
557
558     /*
559      * Method that can be called from the UI thread to update the view model.
560      */

561     private void internalRunViewUpdate(final Runnable JavaDoc runnable, boolean preserveExpansion) {
562         StructuredViewer viewer = getViewer();
563         IResource[] expanded = null;
564         IResource[] selected = null;
565         try {
566             if (Utils.canUpdateViewer(viewer)) {
567                 viewer.getControl().setRedraw(false);
568                 if (preserveExpansion) {
569                     expanded = provider.getExpandedResources();
570                     selected = provider.getSelectedResources();
571                 }
572                 if (viewer instanceof AbstractTreeViewer && additionsMap == null)
573                     additionsMap = new HashMap JavaDoc();
574             }
575             runnable.run();
576         } finally {
577             if (Utils.canUpdateViewer(viewer)) {
578                 try {
579                     if (additionsMap != null && !additionsMap.isEmpty() && Utils.canUpdateViewer(viewer)) {
580                         for (Iterator JavaDoc iter = additionsMap.keySet().iterator(); iter.hasNext();) {
581                             ISynchronizeModelElement parent = (ISynchronizeModelElement) iter.next();
582                             if (DEBUG) {
583                                 System.out.println("Adding child view items of " + parent.getName()); //$NON-NLS-1$
584
}
585                             Set JavaDoc toAdd = (Set JavaDoc)additionsMap.get(parent);
586                             ((AbstractTreeViewer)viewer).add(parent, toAdd.toArray(new Object JavaDoc[toAdd.size()]));
587                         }
588                         additionsMap = null;
589                     }
590                     if (expanded != null) {
591                         provider.expandResources(expanded);
592                     }
593                     if (selected != null) {
594                         provider.selectResources(selected);
595                     }
596                 } finally {
597                     viewer.getControl().setRedraw(true);
598                 }
599             }
600         }
601
602         ISynchronizeModelElement root = provider.getModelRoot();
603         if(root instanceof SynchronizeModelElement)
604             ((SynchronizeModelElement)root).fireChanges();
605     }
606
607     /**
608      * Execute a runnable which performs an update of the model being displayed
609      * by the handler's provider. The runnable should be executed in a thread-safe manner
610      * which esults in the view being updated.
611      * @param runnable the runnable which updates the model.
612      * @param preserveExpansion whether the expansion of the view should be preserver
613      * @param updateInUIThread if <code>true</code>, the model will be updated in the
614      * UI thread. Otherwise, the model will be updated in the handler thread and the view
615      * updated in the UI thread at the end.
616      */

617     public void performUpdate(final IWorkspaceRunnable runnable, boolean preserveExpansion, boolean updateInUIThread) {
618         if (updateInUIThread) {
619             queueEvent(new BackgroundEventHandler.RunnableEvent(getUIUpdateRunnable(runnable, preserveExpansion), true), true);
620         } else {
621             queueEvent(new BackgroundEventHandler.RunnableEvent(getBackgroundUpdateRunnable(runnable, preserveExpansion), true), true);
622         }
623     }
624
625     /**
626      * Wrap the runnable in an outer runnable that preserves expansion.
627      */

628     private IWorkspaceRunnable getUIUpdateRunnable(final IWorkspaceRunnable runnable, final boolean preserveExpansion) {
629         return new IWorkspaceRunnable() {
630             public void run(final IProgressMonitor monitor) throws CoreException {
631                 final CoreException[] exception = new CoreException[] { null };
632                 runViewUpdate(new Runnable JavaDoc() {
633                     public void run() {
634                         try {
635                             runnable.run(monitor);
636                         } catch (CoreException e) {
637                             exception[0] = e;
638                         }
639                     }
640                 }, true /* preserve expansion */);
641                 if (exception[0] != null)
642                     throw exception[0];
643             }
644         };
645     }
646
647     /*
648      * Wrap the runnable in an outer runnable that preserves expansion if requested
649      * and refreshes the view when the update is completed.
650      */

651     private IWorkspaceRunnable getBackgroundUpdateRunnable(final IWorkspaceRunnable runnable, final boolean preserveExpansion) {
652         return new IWorkspaceRunnable() {
653             IResource[] expanded;
654             IResource[] selected;
655             public void run(IProgressMonitor monitor) throws CoreException {
656                 if (preserveExpansion)
657                     recordExpandedResources();
658                 try {
659                     performingBackgroundUpdate = true;
660                     runnable.run(monitor);
661                 } finally {
662                     performingBackgroundUpdate = false;
663                 }
664                 updateView();
665                 
666             }
667             private void recordExpandedResources() {
668                 final StructuredViewer viewer = getViewer();
669                 if (viewer != null && !viewer.getControl().isDisposed() && viewer instanceof AbstractTreeViewer) {
670                     viewer.getControl().getDisplay().syncExec(new Runnable JavaDoc() {
671                         public void run() {
672                             if (viewer != null && !viewer.getControl().isDisposed()) {
673                                 expanded = provider.getExpandedResources();
674                                 selected = provider.getSelectedResources();
675                             }
676                         }
677                     });
678                 }
679             }
680             private void updateView() {
681                 // Refresh the view and then set the expansion
682
runViewUpdate(new Runnable JavaDoc() {
683                     public void run() {
684                         provider.getViewer().refresh();
685                         if (expanded != null)
686                             provider.expandResources(expanded);
687                         if (selected != null)
688                             provider.selectResources(selected);
689                     }
690                 }, false /* do not preserve expansion (since it is done above) */);
691             }
692         };
693     }
694     
695     /*
696      * Execute the RunnableEvent
697      */

698     private void executeRunnable(Event event, IProgressMonitor monitor) {
699         try {
700             // Dispatch any queued results to clear pending output events
701
dispatchEvents(Policy.subMonitorFor(monitor, 1));
702         } catch (TeamException e) {
703             handleException(e);
704         }
705         try {
706             ((RunnableEvent)event).run(Policy.subMonitorFor(monitor, 1));
707         } catch (CoreException e) {
708             handleException(e);
709         }
710     }
711     
712     /**
713      * Add the element to the viewer.
714      * @param parent the parent of the element which is already added to the viewer
715      * @param element the element to be added to the viewer
716      */

717     protected void doAdd(ISynchronizeModelElement parent, ISynchronizeModelElement element) {
718         if (additionsMap == null) {
719             if (DEBUG) {
720                 System.out.println("Added view item " + element.getName()); //$NON-NLS-1$
721
}
722             AbstractTreeViewer viewer = (AbstractTreeViewer)getViewer();
723             viewer.add(parent, element);
724         } else {
725             // Accumulate the additions
726
if (DEBUG) {
727                 System.out.println("Queueing view item for addition " + element.getName()); //$NON-NLS-1$
728
}
729             Set JavaDoc toAdd = (Set JavaDoc)additionsMap.get(parent);
730             if (toAdd == null) {
731                 toAdd = new HashSet JavaDoc();
732                 additionsMap.put(parent, toAdd);
733             }
734             toAdd.add(element);
735         }
736     }
737 }
738
Popular Tags