KickJava   Java API By Example, From Geeks To Geeks.

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


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 java.util.ArrayList JavaDoc;
14 import java.util.HashSet JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Set JavaDoc;
18
19 import org.eclipse.compare.structuremergeviewer.IDiffElement;
20 import org.eclipse.core.resources.*;
21 import org.eclipse.core.runtime.*;
22 import org.eclipse.core.runtime.jobs.Job;
23 import org.eclipse.jface.util.IPropertyChangeListener;
24 import org.eclipse.jface.util.PropertyChangeEvent;
25 import org.eclipse.jface.viewers.*;
26 import org.eclipse.swt.SWTException;
27 import org.eclipse.swt.events.TreeEvent;
28 import org.eclipse.swt.events.TreeListener;
29 import org.eclipse.swt.widgets.Tree;
30 import org.eclipse.team.core.ITeamStatus;
31 import org.eclipse.team.core.synchronize.*;
32 import org.eclipse.team.internal.core.TeamPlugin;
33 import org.eclipse.team.internal.ui.*;
34 import org.eclipse.team.ui.synchronize.*;
35
36 /**
37  * This class is reponsible for creating and maintaining a presentation model of
38  * {@link SynchronizeModelElement} elements that can be shown in a viewer. The model
39  * is based on the synchronization information contained in the provided {@link SyncInfoSet}.
40  */

41 public abstract class AbstractSynchronizeModelProvider implements ISynchronizeModelProvider, ISyncInfoSetChangeListener, TreeListener {
42     
43     /**
44      * Property constant for the expansion state for the elements displayed by the page. The
45      * expansion state is a List of resource paths.
46      */

47     public static final String JavaDoc P_VIEWER_EXPANSION_STATE = TeamUIPlugin.ID + ".P_VIEWER_EXPANSION_STATE"; //$NON-NLS-1$
48

49     /**
50      * Property constant for the selection state for the elements displayed by the page. The
51      * selection state is a List of resource paths.
52      */

53     public static final String JavaDoc P_VIEWER_SELECTION_STATE = TeamUIPlugin.ID + ".P_VIEWER_SELECTION_STATE"; //$NON-NLS-1$
54

55     /**
56      * Property constant for the checked state for the elements displayed by the page. The
57      * checked state is a List of resource paths.
58      */

59     public static final String JavaDoc P_VIEWER_CHECKED_STATE = TeamUIPlugin.ID + ".P_VIEWER_CHECKED_STATE"; //$NON-NLS-1$
60

61     private ISynchronizeModelElement root;
62     
63     private ISynchronizePageConfiguration configuration;
64     
65     private SyncInfoSet set;
66     
67     private SynchronizeModelUpdateHandler updateHandler;
68     
69     private boolean disposed = false;
70
71     private SynchronizePageActionGroup actionGroup;
72
73     private ListenerList listeners;
74     
75     private static final boolean DEBUG = false;
76     
77     /**
78      * Constructor for creating a sub-provider
79      * @param parentProvider the parent provider
80      * @param parentNode the root node of the model built by this provider
81      * @param configuration the sync page configuration
82      * @param set the sync info set from which the model is built
83      */

84     protected AbstractSynchronizeModelProvider(AbstractSynchronizeModelProvider parentProvider, ISynchronizeModelElement parentNode, ISynchronizePageConfiguration configuration, SyncInfoSet set) {
85         Assert.isNotNull(set);
86         Assert.isNotNull(parentNode);
87         this.root = parentNode;
88         this.set = set;
89         this.configuration = configuration;
90         if (parentProvider == null) {
91             // The update handler will register for sync change events
92
// with the sync set when the handler is activated
93
updateHandler = new SynchronizeModelUpdateHandler(this);
94             getTree().addTreeListener(this);
95         } else {
96             // We will use the parent's update handler and register for changes with the given set
97
updateHandler = parentProvider.updateHandler;
98             set.addSyncSetChangedListener(this);
99         }
100     }
101     
102     private Tree getTree() {
103         return ((Tree)((AbstractTreeViewer)getViewer()).getControl());
104     }
105
106     /**
107      * Cosntructor for creating a root model provider.
108      * @param configuration the sync page configuration
109      * @param set the sync info set from which the model is built
110      */

111     protected AbstractSynchronizeModelProvider(ISynchronizePageConfiguration configuration, SyncInfoSet set) {
112         this(null, new UnchangedResourceModelElement(null, ResourcesPlugin.getWorkspace().getRoot()) {
113             /*
114              * Override to ensure that the diff viewer will appear in CompareEditorInputs
115              */

116             public boolean hasChildren() {
117                 return true;
118             }
119         }, configuration, set);
120         // Register the action group for this provider, since it is the root provider
121
SynchronizePageActionGroup actionGroup = getActionGroup();
122         if (actionGroup != null) {
123             configuration.addActionContribution(actionGroup);
124         }
125     }
126     
127     /**
128      * Return the action group for this provider or <code>null</code>
129      * if there are no actions associated with this provider. The action
130      * group will be registered with the configuration if this is
131      * the root provider. If this provider is a sub-provider, it
132      * is up to the parent provider to register the action group.
133      * <p>
134      * The action group for a provider is created by calling the
135      * <code>createdActionGroup</code> method. If this method returns
136      * a non-null group, it is cached so it can be disposed
137      * when the provider is disposed.
138      * @return the action group for this provider or <code>null</code>
139      * if there are no actions associated with this provider
140      */

141     public final synchronized SynchronizePageActionGroup getActionGroup() {
142         if (actionGroup == null) {
143             actionGroup = createActionGroup();
144         }
145         return actionGroup;
146     }
147
148     /**
149      * Create the action group for this provider. By default,
150      * a <code>null</code> is returned. Subclasses may override.
151      * @return the action group for this provider or <code>null</code>
152      */

153     protected SynchronizePageActionGroup createActionGroup() {
154         return null;
155     }
156     
157     /**
158      * Return the set that contains the elements this provider is using as
159      * a basis for creating a presentation model. This cannot be null.
160      *
161      * @return the set that contains the elements this provider is
162      * using as a basis for creating a presentation model.
163      */

164     public SyncInfoSet getSyncInfoSet() {
165         return set;
166     }
167     
168     /**
169      * Returns the input created by this provider or <code>null</code> if
170      * {@link #prepareInput(IProgressMonitor)} hasn't been called on this object yet.
171      *
172      * @return the input created by this provider.
173      */

174     public ISynchronizeModelElement getModelRoot() {
175         return root;
176     }
177     
178     /**
179      * Return the page configuration for this provider.
180      *
181      * @return the page configuration for this provider.
182      */

183     public ISynchronizePageConfiguration getConfiguration() {
184         return configuration;
185     }
186     
187     /**
188      * Return the <code>AbstractTreeViewer</code> associated with this
189      * provider or <code>null</code> if the viewer is not of the proper type.
190      * @return the structured viewer that is displaying the model managed by this provider
191      */

192     public StructuredViewer getViewer() {
193         ISynchronizePage page = configuration.getPage();
194         if (page == null) return null;
195         Viewer viewer = page.getViewer();
196         if (viewer instanceof AbstractTreeViewer) {
197             return (AbstractTreeViewer)viewer;
198         }
199         return null;
200     }
201
202     /**
203      * Builds the viewer model based on the contents of the sync set.
204      */

205     public ISynchronizeModelElement prepareInput(IProgressMonitor monitor) {
206         // Connect to the sync set which will register us as a listener and give us a reset event
207
// in a background thread
208
if (isRootProvider()) {
209             updateHandler.connect(monitor);
210         } else {
211             getSyncInfoSet().connect(this, monitor);
212         }
213         return getModelRoot();
214     }
215     
216     /**
217      * Calculate the problem marker that should be shown on the given
218      * element. The returned property can be either
219      * ISynchronizeModelElement.PROPAGATED_ERROR_MARKER_PROPERTY or
220      * ISynchronizeModelElement.PROPAGATED_WARNING_MARKER_PROPERTY.
221      * @param element a synchronize model element
222      * @return the marker property that should be displayed on the element
223      * or <code>null</code> if no marker should be displayed
224      */

225     public String JavaDoc calculateProblemMarker(ISynchronizeModelElement element) {
226         IResource resource = element.getResource();
227         String JavaDoc property = null;
228         if (resource != null && resource.exists()) {
229             try {
230                 IMarker[] markers = resource.findMarkers(IMarker.PROBLEM, true, getLogicalModelDepth(resource));
231                 for (int i = 0; i < markers.length; i++) {
232                     IMarker marker = markers[i];
233                     try {
234                         Integer JavaDoc severity = (Integer JavaDoc) marker.getAttribute(IMarker.SEVERITY);
235                         if (severity != null) {
236                             if (severity.intValue() == IMarker.SEVERITY_ERROR) {
237                                 property = ISynchronizeModelElement.PROPAGATED_ERROR_MARKER_PROPERTY;
238                                 break;
239                             } else if (severity.intValue() == IMarker.SEVERITY_WARNING) {
240                                 property = ISynchronizeModelElement.PROPAGATED_WARNING_MARKER_PROPERTY;
241                                 // Keep going because there may be errors on other resources
242
}
243                         }
244                     } catch (CoreException e) {
245                         if (!resource.exists()) {
246                             // The resource was deleted concurrently. Forget any previously found property
247
property = null;
248                             break;
249                         }
250                         // If the marker exists, log the exception and continue.
251
// Otherwise, just ignore the exception and keep going
252
if (marker.exists()) {
253                             TeamPlugin.log(e);
254                         }
255                     }
256                 }
257             } catch (CoreException e) {
258                 // If the resource exists (is accessible), log the exception and continue.
259
// Otherwise, just ignore the exception
260
if (resource.isAccessible()
261                         && e.getStatus().getCode() != IResourceStatus.RESOURCE_NOT_FOUND
262                         && e.getStatus().getCode() != IResourceStatus.PROJECT_NOT_OPEN) {
263                     TeamPlugin.log(e);
264                 }
265             }
266         } else if (resource == null) {
267             // For non-resource elements, show the same propogaqted marker as the children
268
IDiffElement[] children = element.getChildren();
269             for (int i = 0; i < children.length; i++) {
270                 IDiffElement child = children[i];
271                 if (child instanceof ISynchronizeModelElement) {
272                     ISynchronizeModelElement childElement = (ISynchronizeModelElement)child;
273                     if (childElement.getProperty(ISynchronizeModelElement.PROPAGATED_ERROR_MARKER_PROPERTY)) {
274                         property = ISynchronizeModelElement.PROPAGATED_ERROR_MARKER_PROPERTY;
275                         break;
276                     } else if (childElement.getProperty(ISynchronizeModelElement.PROPAGATED_WARNING_MARKER_PROPERTY)) {
277                         property = ISynchronizeModelElement.PROPAGATED_WARNING_MARKER_PROPERTY;
278                         // Keep going because there may be errors on other resources
279
}
280                     
281                 }
282             }
283         }
284         return property;
285     }
286     
287     /**
288      * Return the logical model depth used for marker propogation
289      * @param resource the resoure
290      * @return the depth the resources should be traversed
291      */

292     protected int getLogicalModelDepth(IResource resource) {
293         return IResource.DEPTH_INFINITE;
294     }
295     
296     /**
297      * Update the label of the given diff node. The label for nodes queued
298      * using this method will not be updated until <code>firePendingLabelUpdates</code>
299      * is called.
300      * @param diffNode the diff node to be updated
301      */

302     protected void queueForLabelUpdate(ISynchronizeModelElement diffNode) {
303         updateHandler.queueForLabelUpdate(diffNode);
304     }
305     
306     /**
307      * Throw away any old state associated with this provider and
308      * rebuild the model from scratch.
309      */

310     protected void reset() {
311         // save expansion state
312
if(isRootProvider() && hasViewerState()) {
313             saveViewerState();
314         }
315         
316         // Clear existing model, but keep the root node
317
clearModelObjects(getModelRoot());
318         
319         // Rebuild the model
320
buildModelObjects(getModelRoot());
321         
322         // Notify listeners that model has changed
323
ISynchronizeModelElement root = getModelRoot();
324         if(root instanceof SynchronizeModelElement) {
325             ((SynchronizeModelElement)root).fireChanges();
326         }
327         
328         if (Utils.canUpdateViewer(getViewer())) {
329             // If we can update the viewer, that means that the view was updated
330
// when the model was rebuilt.
331
refreshModelRoot();
332         } else {
333             // Only refresh the view if there is now background update in
334
// progress. If there is, the background update will refresh
335
if (!updateHandler.isPerformingBackgroundUpdate()) {
336                 Utils.asyncExec(new Runnable JavaDoc() {
337                     public void run() {
338                         refreshModelRoot();
339                     }
340                 }, getViewer());
341             }
342         }
343     }
344     
345     private void refreshModelRoot() {
346         StructuredViewer viewer = getViewer();
347         if (viewer != null && !viewer.getControl().isDisposed()) {
348             try {
349                 viewer.getControl().setRedraw(false);
350                 if (isRootProvider() || getModelRoot().getParent() == null) {
351                     // Refresh the entire view
352
viewer.refresh();
353                 } else {
354                     // Only refresh the model root bu also ensure that
355
// the parents of the model root and the model root
356
// itself are added to the view
357
addToViewer(getModelRoot());
358                 }
359                 // restore expansion state
360
if (isRootProvider())
361                     restoreViewerState();
362             } finally {
363                 viewer.getControl().setRedraw(true);
364             }
365         }
366     }
367     
368     /**
369      * For each node create children based on the contents of
370      * @param node
371      * @return the diff elements
372      */

373     protected abstract IDiffElement[] buildModelObjects(ISynchronizeModelElement node);
374     
375     /**
376      * Returns whether the viewer has state to be saved.
377      * @return whether the viewer has state to be saved
378      */

379     protected abstract boolean hasViewerState();
380
381     /*
382      * Return all the resources that are expanded in the page.
383      * This method should only be called in the UI thread
384      * after validating that the viewer is still valid.
385      */

386     protected IResource[] getExpandedResources() {
387         Set JavaDoc expanded = new HashSet JavaDoc();
388         IResource[] savedExpansionState = getCachedResources(P_VIEWER_EXPANSION_STATE);
389         for (int i = 0; i < savedExpansionState.length; i++) {
390             IResource resource = savedExpansionState[i];
391             expanded.add(resource);
392         }
393         StructuredViewer viewer = getViewer();
394         Object JavaDoc[] objects = ((AbstractTreeViewer) viewer).getVisibleExpandedElements();
395         IResource[] currentExpansionState = getResources(objects);
396         for (int i = 0; i < currentExpansionState.length; i++) {
397             IResource resource = currentExpansionState[i];
398             expanded.add(resource);
399         }
400         return (IResource[]) expanded.toArray(new IResource[expanded.size()]);
401     }
402     
403     /*
404      * Return all the resources that are selected in the page.
405      * This method should only be called in the UI thread
406      * after validating that the viewer is still valid.
407      */

408     protected IResource[] getSelectedResources() {
409         StructuredViewer viewer = getViewer();
410         return getResources(((IStructuredSelection) viewer.getSelection()).toArray());
411     }
412     
413     /*
414      * Return all the resources that are checked in the page.
415      * This method should only be called in the UI thread
416      * after validating that the viewer is still valid.
417      */

418     protected IResource[] getCheckedResources() {
419         StructuredViewer viewer = getViewer();
420         if (viewer instanceof CheckboxTreeViewer){
421             return getResources(((CheckboxTreeViewer)viewer).getCheckedElements());
422         }
423         
424         return new IResource[0];
425     }
426     
427     /*
428      * Expand the resources if they appear in the page.
429      * This method should only be called in the UI thread
430      * after validating that the viewer is still valid.
431      */

432     protected void expandResources(IResource[] resources) {
433         Set JavaDoc expandedElements = new HashSet JavaDoc();
434         StructuredViewer viewer = getViewer();
435         for (int j = 0; j < resources.length; j++) {
436             IResource resource = resources[j];
437             ISynchronizeModelElement[] elements = getModelObjects(resource);
438             // Only expand when there is one element per resource
439
if (elements.length == 1) {
440                 for (int i = 0; i < elements.length; i++) {
441                     ISynchronizeModelElement element = elements[i];
442                     // Add all parents of the element to the expansion set
443
while (element != null) {
444                         expandedElements.add(element);
445                         element = (ISynchronizeModelElement)element.getParent();
446                     }
447                 }
448             }
449         }
450         if (!expandedElements.isEmpty())
451             ((AbstractTreeViewer) viewer).setExpandedElements(expandedElements.toArray());
452     }
453     
454     protected IResource[] getResources(Object JavaDoc[] objects) {
455         Set JavaDoc result = new HashSet JavaDoc();
456         if (objects.length > 0) {
457             for (int i = 0; i < objects.length; i++) {
458                 if (objects[i] instanceof ISynchronizeModelElement) {
459                     IResource resource = ((ISynchronizeModelElement)objects[i]).getResource();
460                     if(resource != null)
461                         result.add(resource);
462                 }
463             }
464         }
465         return (IResource[]) result.toArray(new IResource[result.size()]);
466     }
467     
468     private void clearResourceCache(String JavaDoc configProperty) {
469         getConfiguration().setProperty(configProperty, null);
470     }
471     
472     private void cacheResources(IResource[] resources, String JavaDoc configProperty) {
473         if (resources.length > 0) {
474             ISynchronizePageConfiguration config = getConfiguration();
475             ArrayList JavaDoc paths = new ArrayList JavaDoc();
476             for (int i = 0; i < resources.length; i++) {
477                 IResource resource = resources[i];
478                 String JavaDoc path = resource.getFullPath().toString();
479                 if (resource.getType() != IResource.FILE && path.charAt(path.length() - 1) != IPath.SEPARATOR) {
480                     // Include a trailing slash on folders and projects.
481
// It is used when recreating cached resources that don't exist locally
482
path += IPath.SEPARATOR;
483                 }
484                 paths.add(path);
485             }
486             config.setProperty(configProperty, paths);
487         } else {
488             clearResourceCache(configProperty);
489         }
490     }
491     
492     private IResource[] getCachedResources(String JavaDoc configProperty) {
493         List JavaDoc paths = (List JavaDoc)getConfiguration().getProperty(configProperty);
494         if (paths == null)
495             return new IResource[0];
496         IContainer container = ResourcesPlugin.getWorkspace().getRoot();
497         ArrayList JavaDoc resources = new ArrayList JavaDoc();
498         for (Iterator JavaDoc it = paths.iterator(); it.hasNext();) {
499             String JavaDoc path = (String JavaDoc) it.next();
500             IResource resource = getResourceForPath(container, path);
501             if (resource != null) {
502                 resources.add(resource);
503             }
504         }
505         return (IResource[]) resources.toArray(new IResource[resources.size()]);
506     }
507     
508     /**
509      * Save the viewer state (expansion and selection)
510      */

511     protected void saveViewerState() {
512         // save visible expanded elements and selection
513
final StructuredViewer viewer = getViewer();
514         if (viewer != null && !viewer.getControl().isDisposed() && viewer instanceof AbstractTreeViewer) {
515             //check to see if we should store the checked states of the tree
516

517             final boolean storeChecks = ((SynchronizePageConfiguration)configuration).getViewerStyle() == SynchronizePageConfiguration.CHECKBOX;
518             final IResource[][] expandedResources = new IResource[1][0];
519             final IResource[][] selectedResources = new IResource[1][0];
520             final IResource[][] checkedResources = new IResource[1][0];
521             viewer.getControl().getDisplay().syncExec(new Runnable JavaDoc() {
522                 public void run() {
523                     if (viewer != null && !viewer.getControl().isDisposed()) {
524                         expandedResources[0] = getExpandedResources();
525                         selectedResources[0] = getSelectedResources();
526                         if (storeChecks)
527                             checkedResources [0] = getCheckedResources();
528                     }
529                 }
530             });
531             
532             // Save expansion and selection
533
cacheResources(expandedResources[0], P_VIEWER_EXPANSION_STATE);
534             cacheResources(selectedResources[0], P_VIEWER_SELECTION_STATE);
535             if (storeChecks)
536                 cacheResources(checkedResources[0], P_VIEWER_CHECKED_STATE);
537         }
538     }
539
540     /**
541      * Restore the expansion state and seleciton of the viewer.
542      * This method must be invoked from within the UI thread.
543      */

544     protected void restoreViewerState() {
545         // restore expansion state and selection state
546
final StructuredViewer viewer = getViewer();
547         if (viewer != null && !viewer.getControl().isDisposed() && viewer instanceof AbstractTreeViewer) {
548             IResource[] resourcesToExpand = getCachedResources(P_VIEWER_EXPANSION_STATE);
549             IResource[] resourcesToSelect = getCachedResources(P_VIEWER_SELECTION_STATE);
550             if (((SynchronizePageConfiguration)configuration).getViewerStyle() == SynchronizePageConfiguration.CHECKBOX){
551                 IResource[] resourcesToCheck = getCachedResources(P_VIEWER_CHECKED_STATE);
552                 checkResources(resourcesToCheck);
553             }
554             expandResources(resourcesToExpand);
555             selectResources(resourcesToSelect);
556         }
557     }
558
559     /*
560      * Select the given resources in the view. This method can
561      * only be invoked from the UI thread.
562      */

563     protected void selectResources(IResource[] resourcesToSelect) {
564         StructuredViewer viewer = getViewer();
565         final ArrayList JavaDoc selectedElements = new ArrayList JavaDoc();
566         for (int i = 0; i < resourcesToSelect.length; i++) {
567             IResource resource = resourcesToSelect[i];
568             ISynchronizeModelElement[] elements = getModelObjects(resource);
569             // Only preserve the selection if there is one element for the resource
570
if (elements.length == 1) {
571                 selectedElements.add(elements[0]);
572             }
573         }
574         if (!selectedElements.isEmpty())
575             viewer.setSelection(new StructuredSelection(selectedElements));
576     }
577
578     /*
579      * Check the given resources in the view. This method can
580      * only be invoked from the UI thread.
581      */

582     protected void checkResources(IResource[] resourcesToCheck) {
583          Set JavaDoc checkedElements = new HashSet JavaDoc();
584          StructuredViewer viewer = getViewer();
585          if (!(viewer instanceof CheckboxTreeViewer))
586              return;
587          
588          for (int j = 0; j < resourcesToCheck.length; j++) {
589              IResource resource = resourcesToCheck[j];
590              if (resource.getType() != IResource.FILE)
591                  continue;
592              
593              ISynchronizeModelElement[] elements = getModelObjects(resource);
594              // Only expand when there is one element per resource
595
if (elements.length == 1) {
596                 for (int i = 0; i < elements.length; i++) {
597                      ISynchronizeModelElement element = elements[i];
598                      checkedElements.add(element);
599                  }
600              }
601         }
602          if (!checkedElements.isEmpty())
603              ((CheckboxTreeViewer) viewer).setCheckedElements(checkedElements.toArray());
604     }
605     
606     /*
607      * Convert a path to a resource by first looking in the resource
608      * tree and, if that fails, by using the path format to create
609      * a handle.
610      */

611     private IResource getResourceForPath(IContainer container, String JavaDoc path) {
612         IResource resource = container.findMember(path, true /* include phantoms */);
613         if (resource == null) {
614             try {
615                 // The resource doesn't have an entry on the resources tree
616
// but may still appear in the view so try to deduce the type
617
// from the path
618
if (path.endsWith(Character.toString(IPath.SEPARATOR))) {
619                     resource = container.getFolder(new Path(null, path));
620                 } else {
621                     resource = container.getFile(new Path(null, path));
622                 }
623             } catch (IllegalArgumentException JavaDoc e) {
624                 // Couldn't get a resource handle so ignore
625
}
626         }
627         return resource;
628     }
629
630     /* (non-Javadoc)
631      * @see org.eclipse.swt.events.TreeListener#treeCollapsed(org.eclipse.swt.events.TreeEvent)
632      */

633     public void treeCollapsed(TreeEvent e) {
634         clearResourceCache(P_VIEWER_EXPANSION_STATE);
635     }
636     
637     /* (non-Javadoc)
638      * @see org.eclipse.swt.events.TreeListener#treeExpanded(org.eclipse.swt.events.TreeEvent)
639      */

640     public void treeExpanded(TreeEvent e) {
641         clearResourceCache(P_VIEWER_EXPANSION_STATE);
642     }
643     
644     /**
645      * Return all the model objects in this provider that represent the given resource
646      * @param resource the resource
647      * @return the model objects for the resource
648      */

649     protected abstract ISynchronizeModelElement[] getModelObjects(IResource resource);
650
651     /* (non-Javadoc)
652      * @see org.eclipse.team.internal.ui.synchronize.ISynchronizeModelProvider#saveState()
653      */

654     public void saveState() {
655         saveViewerState();
656     }
657     
658     /**
659      * Method invoked when a sync element is added or removed or its state changes.
660      * This method can be invoked from the UI thread or a background thread.
661      * @param element synchronize element
662      * @param clear <code>true</code> if the conflict bit of the element was cleared
663      * (i.e. the element has been deleted)
664      */

665     protected void propogateConflictState(ISynchronizeModelElement element, boolean clear) {
666         boolean isConflict = clear ? false : isConflicting(element);
667         boolean wasConflict = element.getProperty(ISynchronizeModelElement.PROPAGATED_CONFLICT_PROPERTY);
668         // Only propogate and update parent labels if the state of the element has changed
669
if (isConflict != wasConflict) {
670             element.setPropertyToRoot(ISynchronizeModelElement.PROPAGATED_CONFLICT_PROPERTY, isConflict);
671             updateHandler.updateParentLabels(element);
672         }
673     }
674     
675     /**
676      * Return whether the given model element represets a conflict.
677      * @param element the element being tested
678      * @return whether the element is a conflict
679      */

680     protected boolean isConflicting(ISynchronizeModelElement element) {
681         return (element.getKind() & SyncInfo.DIRECTION_MASK) == SyncInfo.CONFLICTING;
682     }
683     
684     /**
685      * Dispose of the provider
686      */

687     public void dispose() {
688         // Only dispose the update handler if it is
689
// directly associated with this provider
690
if (isRootProvider()) {
691             updateHandler.dispose();
692             getTree().removeTreeListener(this);
693         } else {
694             set.removeSyncSetChangedListener(this);
695         }
696         if (actionGroup != null) {
697             Utils.syncExec(new Runnable JavaDoc() {
698                 public void run() {
699                     actionGroup.dispose();
700                 }
701             }, getViewer());
702         }
703         this.disposed = true;
704     }
705     
706     private boolean isRootProvider() {
707         return updateHandler.getProvider() == this;
708     }
709
710     /**
711      * Return whether this provide has been disposed.
712      * @return whether this provide has been disposed
713      */

714     public boolean isDisposed() {
715         return disposed;
716     }
717
718     /**
719      * Return the closest parent elements that represents a model element that
720      * could contains the given resource. Multiple elements need only be returned
721      * if two or more logical views are being shown and each view has an element
722      * that could contain the resource.
723      * @param resource the resource
724      * @return one or more lowest level parents that could contain the resource
725      */

726     public abstract ISynchronizeModelElement[] getClosestExistingParents(IResource resource);
727     
728     /**
729      * Handle the changes made to the viewer's <code>SyncInfoSet</code>.
730      * This method delegates the changes to the three methods <code>handleResourceChanges(ISyncInfoSetChangeEvent)</code>,
731      * <code>handleResourceRemovals(ISyncInfoSetChangeEvent)</code> and
732      * <code>handleResourceAdditions(ISyncInfoSetChangeEvent)</code>.
733      * @param event
734      * the event containing the changed resourcses.
735      */

736     protected void handleChanges(ISyncInfoTreeChangeEvent event, IProgressMonitor monitor) {
737         handleResourceChanges(event);
738         handleResourceRemovals(event);
739         handleResourceAdditions(event);
740     }
741
742     /**
743      * Update the viewer for the sync set additions in the provided event. This
744      * method is invoked by <code>handleChanges(ISyncInfoSetChangeEvent)</code>.
745      * Subclasses may override.
746      * @param event
747      */

748     protected abstract void handleResourceAdditions(ISyncInfoTreeChangeEvent event);
749
750     /**
751      * Update the viewer for the sync set changes in the provided event. This
752      * method is invoked by <code>handleChanges(ISyncInfoSetChangeEvent)</code>.
753      * Subclasses may override.
754      * @param event
755      */

756     protected abstract void handleResourceChanges(ISyncInfoTreeChangeEvent event);
757
758     /**
759      * Update the viewer for the sync set removals in the provided event. This
760      * method is invoked by <code>handleChanges(ISyncInfoSetChangeEvent)</code>.
761      * Subclasses may override.
762      * @param event
763      */

764     protected abstract void handleResourceRemovals(ISyncInfoTreeChangeEvent event);
765     
766     /* (non-Javadoc)
767      * @see org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener#syncInfoChanged(org.eclipse.team.core.synchronize.ISyncInfoSetChangeEvent, org.eclipse.core.runtime.IProgressMonitor)
768      */

769     public void syncInfoChanged(final ISyncInfoSetChangeEvent event, final IProgressMonitor monitor) {
770         if (! (event instanceof ISyncInfoTreeChangeEvent)) {
771             reset();
772         } else {
773             updateHandler.runViewUpdate(new Runnable JavaDoc() {
774                 public void run() {
775                     handleChanges((ISyncInfoTreeChangeEvent)event, monitor);
776                 }
777             }, true /* preserve expansion */);
778         }
779     }
780     
781     /* (non-Javadoc)
782      * @see org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener#syncInfoSetErrors(org.eclipse.team.core.synchronize.SyncInfoSet, org.eclipse.team.core.ITeamStatus[], org.eclipse.core.runtime.IProgressMonitor)
783      */

784     public void syncInfoSetErrors(SyncInfoSet set, ITeamStatus[] errors, IProgressMonitor monitor) {
785         // Not handled
786

787     }
788     
789     /* (non-Javadoc)
790      * @see org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener#syncInfoSetReset(org.eclipse.team.core.synchronize.SyncInfoSet, org.eclipse.core.runtime.IProgressMonitor)
791      */

792     public void syncInfoSetReset(SyncInfoSet set, IProgressMonitor monitor) {
793         reset();
794     }
795     
796     protected void addToViewer(ISynchronizeModelElement node) {
797         if (DEBUG) {
798             System.out.println("Adding model element " + node.getName()); //$NON-NLS-1$
799
}
800         propogateConflictState(node, false);
801         // Set the marker property on this node.
802
// There is no need to propogate this to the parents
803
// as they will be displaying the proper marker already
804
String JavaDoc property = calculateProblemMarker(node);
805         if (property != null) {
806             node.setProperty(property, true);
807             // Parent resource nodes would have been properly calculated when they were added.
808
// However, non-resource nodes would not so we need to propogate the marker to them
809
propogateMarkerPropertyToParent(node, property);
810         }
811         if (Utils.canUpdateViewer(getViewer())) {
812             doAdd((SynchronizeModelElement)node.getParent(), node);
813         }
814         updateHandler.nodeAdded(node, this);
815     }
816     
817     /*
818      * Propogate the marker property to the parent if it is not already there.
819      * Only propogate warnings if the parent isn't an error already.
820      */

821     private void propogateMarkerPropertyToParent(ISynchronizeModelElement node, String JavaDoc property) {
822         ISynchronizeModelElement parent = (ISynchronizeModelElement)node.getParent();
823         if (parent != null
824                 && !parent.getProperty(property)
825                 && !parent.getProperty(ISynchronizeModelElement.PROPAGATED_ERROR_MARKER_PROPERTY)) {
826             parent.setProperty(property, true);
827             propogateMarkerPropertyToParent(parent, property);
828         }
829     }
830
831     /**
832      * Remove any traces of the model element and any of it's descendants in the
833      * hiearchy defined by the content provider from the content provider and
834      * the viewer it is associated with.
835      * @param nodes the model elements to remove
836      */

837     protected void removeFromViewer(ISynchronizeModelElement[] nodes) {
838         List JavaDoc rootsToClear = new ArrayList JavaDoc();
839         for (int i = 0; i < nodes.length; i++) {
840             ISynchronizeModelElement node = nodes[i];
841             if (DEBUG) {
842                 System.out.println("Removing model element " + node.getName()); //$NON-NLS-1$
843
}
844             ISynchronizeModelElement rootToClear = getRootToClear(node);
845             if (DEBUG && rootToClear != node) {
846                 System.out.println("Removing parent element " + rootToClear.getName()); //$NON-NLS-1$
847
}
848             propogateConflictState(rootToClear, true /* clear the conflict */);
849             clearModelObjects(rootToClear);
850             rootsToClear.add(rootToClear);
851         }
852         ISynchronizeModelElement[] roots = (ISynchronizeModelElement[]) rootsToClear.toArray(new ISynchronizeModelElement[rootsToClear.size()]);
853         if (Utils.canUpdateViewer(getViewer())) {
854             doRemove(roots);
855         }
856         for (int i = 0; i < roots.length; i++) {
857             ISynchronizeModelElement element = roots[i];
858             updateHandler.nodeRemoved(element, this);
859         }
860     }
861     
862     /**
863      * Clear the model objects from the diff tree, cleaning up any cached state
864      * (such as resource to model object map). This method recurses deeply on
865      * the tree to allow the cleanup of any cached state for the children as
866      * well.
867      * @param node the root node
868      */

869     protected final void clearModelObjects(ISynchronizeModelElement node) {
870         // When clearing model objects, any parents of the node
871
// That are not out-of-sync, not the model root and that would
872
// be empty as a result of this clear, should also be cleared.
873
ISynchronizeModelElement rootToClear = getRootToClear(node);
874         // Recursively clear the nodes from the root
875
recursiveClearModelObjects(rootToClear);
876         if (node == getModelRoot()) {
877             IDiffElement[] children = node.getChildren();
878             for (int i = 0; i < children.length; i++) {
879                 IDiffElement element = children[i];
880                 ((SynchronizeModelElement)node).remove(element);
881             }
882         } else {
883             SynchronizeModelElement parent = ((SynchronizeModelElement)node.getParent());
884             if (parent != null) parent.remove(node);
885         }
886     }
887     
888     /**
889      * Method that sublcasses can oiverride when clearing model objects.
890      * @param node the node to be cleared recursively
891      */

892     protected void recursiveClearModelObjects(ISynchronizeModelElement node) {
893         // Clear all the children of the node
894
IDiffElement[] children = node.getChildren();
895         for (int i = 0; i < children.length; i++) {
896             IDiffElement element = children[i];
897             if (element instanceof ISynchronizeModelElement) {
898                 ISynchronizeModelElement sme = (ISynchronizeModelElement) element;
899                 ISynchronizeModelProvider provider = getProvider(sme);
900                 if (provider != null && provider instanceof AbstractSynchronizeModelProvider) {
901                     ((AbstractSynchronizeModelProvider)provider).recursiveClearModelObjects(sme);
902                 } else {
903                     recursiveClearModelObjects(sme);
904                 }
905             }
906         }
907         // Notify the update handler that the node has been cleared
908
if (node != getModelRoot())
909             updateHandler.modelObjectCleared(node);
910     }
911
912     /*
913      * Remove to root should only remove to the root of the provider and not the
914      * diff tree.
915      */

916     private ISynchronizeModelElement getRootToClear(ISynchronizeModelElement node) {
917         if (node == getModelRoot()) return node;
918         ISynchronizeModelElement parent = (ISynchronizeModelElement)node.getParent();
919         if (parent != null && parent != getModelRoot() && !isOutOfSync(parent) && parent.getChildren().length == 1) {
920             return getRootToClear(parent);
921         }
922         return node;
923     }
924
925     /*
926      * Return whether the node represents an out-of-sync resource.
927      */

928     protected boolean isOutOfSync(ISynchronizeModelElement node) {
929         SyncInfo info = Utils.getSyncInfo(node);
930         return (info != null && info.getKind() != SyncInfo.IN_SYNC);
931     }
932
933     protected boolean isOutOfSync(IResource resource) {
934         SyncInfo info = getSyncInfoSet().getSyncInfo(resource);
935         return (info != null && info.getKind() != SyncInfo.IN_SYNC);
936     }
937     
938     /**
939      * Return the provider that created and manages the given
940      * model element. The default is to return the receiver.
941      * Subclasses may override.
942      * @param element the synchronizew model element
943      * @return the provider that created the element
944      */

945     protected ISynchronizeModelProvider getProvider(ISynchronizeModelElement element) {
946         return this;
947     }
948
949     /**
950      * Add the element to the viewer.
951      * @param parent the parent of the element which is already added to the viewer
952      * @param element the element to be added to the viewer
953      */

954     protected void doAdd(ISynchronizeModelElement parent, ISynchronizeModelElement element) {
955         updateHandler.doAdd(parent, element);
956     }
957     
958     /**
959      * Remove the element from the viewer
960      * @param elements the elements to be removed
961      */

962     protected void doRemove(ISynchronizeModelElement[] elements) {
963         AbstractTreeViewer viewer = (AbstractTreeViewer)getViewer();
964         try {
965             viewer.remove(elements);
966         } catch (SWTException e) {
967             // The remove failed due to an SWT exception. Log it and continue
968
TeamUIPlugin.log(IStatus.ERROR, "An error occurred removing elements from the synchronize view", e); //$NON-NLS-1$
969
}
970         if (DEBUG) {
971             for (int i = 0; i < elements.length; i++) {
972                 ISynchronizeModelElement element = elements[i];
973                 System.out.println("Removing view item " + element.getName()); //$NON-NLS-1$
974
}
975         }
976     }
977     
978     /**
979      * This is a callback from the model update handler that gets invoked
980      * when a node is added to the viewer. It is only invoked for the
981      * root level model provider.
982      * @param node
983      * @param provider the provider that added the node
984      */

985     protected void nodeAdded(ISynchronizeModelElement node, AbstractSynchronizeModelProvider provider) {
986         // Default is to do nothing
987
}
988     
989     /**
990      * This is a callback from the model update handler that gets invoked
991      * when a node is removed from the viewer. It is only invoked for the
992      * root level model provider. A removed node may have children for
993      * which a <code>nodeRemoved</code> callback is not received (see
994      * <code>modelObjectCleared</code>).
995      * @param node
996      */

997     protected void nodeRemoved(ISynchronizeModelElement node, AbstractSynchronizeModelProvider provider) {
998         // Default is to do nothing
999
}
1000    
1001    /**
1002     * This is a callback from the model update handler that gets invoked
1003     * when a node is cleared from the model. It is only invoked for the
1004     * root level model provider. This callback is deep in the sense that
1005     * a callback is sent for each node that is cleared.
1006     * @param node the node that was cleared.
1007     */

1008    public void modelObjectCleared(ISynchronizeModelElement node) {
1009        // Default is to do nothing
1010
}
1011    
1012    public void addPropertyChangeListener(IPropertyChangeListener listener) {
1013        synchronized (this) {
1014            if (listeners == null) {
1015                listeners = new ListenerList(ListenerList.IDENTITY);
1016            }
1017            listeners.add(listener);
1018        }
1019
1020    }
1021    public void removePropertyChangeListener(IPropertyChangeListener listener) {
1022        if (listeners != null) {
1023            synchronized (this) {
1024                listeners.remove(listener);
1025                if (listeners.isEmpty()) {
1026                    listeners = null;
1027                }
1028            }
1029        }
1030    }
1031    
1032    protected void firePropertyChange(String JavaDoc key, Object JavaDoc oldValue, Object JavaDoc newValue) {
1033        Object JavaDoc[] allListeners;
1034        synchronized(this) {
1035            allListeners = listeners.getListeners();
1036        }
1037        final PropertyChangeEvent event = new PropertyChangeEvent(this, key, oldValue, newValue);
1038        for (int i = 0; i < allListeners.length; i++) {
1039            final IPropertyChangeListener listener = (IPropertyChangeListener)allListeners[i];
1040            SafeRunner.run(new ISafeRunnable() {
1041                public void handleException(Throwable JavaDoc exception) {
1042                    // Error is logged by platform
1043
}
1044                public void run() throws Exception JavaDoc {
1045                    listener.propertyChange(event);
1046                }
1047            });
1048        }
1049    }
1050    
1051    /**
1052     * Wait until the provider is done processing any events and
1053     * the page conent are up-to-date.
1054     * This method is for testing purposes only.
1055     * @param monitor
1056     */

1057    public void waitUntilDone(IProgressMonitor monitor) {
1058        monitor.worked(1);
1059        // wait for the event handler to process changes.
1060
while(updateHandler.getEventHandlerJob().getState() != Job.NONE) {
1061            monitor.worked(1);
1062            try {
1063                Thread.sleep(10);
1064            } catch (InterruptedException JavaDoc e) {
1065            }
1066            Policy.checkCanceled(monitor);
1067        }
1068        monitor.worked(1);
1069    }
1070    
1071    /* (non-Javadoc)
1072     * @see java.lang.Object#toString()
1073     */

1074    public String JavaDoc toString() {
1075        ISynchronizeModelElement element = getModelRoot();
1076        String JavaDoc name = getClass().getName();
1077        int index = name.lastIndexOf("."); //$NON-NLS-1$
1078
if (index != -1) {
1079            name = name.substring(index + 1);
1080        }
1081        String JavaDoc name2 = element.getName();
1082        if (name2.length() == 0) {
1083            name2 = "/"; //$NON-NLS-1$
1084
}
1085        return name + ": " + name2; //$NON-NLS-1$
1086
}
1087    
1088    /**
1089     * Execute a runnable which performs an update of the model being displayed
1090     * by this provider. The runnable should be executed in a thread-safe manner
1091     * which esults in the view being updated.
1092     * @param runnable the runnable which updates the model.
1093     * @param preserveExpansion whether the expansion of the view should be preserver
1094     * @param runInUIThread
1095     */

1096    public void performUpdate(IWorkspaceRunnable runnable, boolean preserveExpansion, boolean runInUIThread) {
1097        updateHandler.performUpdate(runnable, preserveExpansion, runInUIThread);
1098    }
1099}
1100
Popular Tags