KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > core > mapping > provider > SynchronizationScopeManager


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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.core.mapping.provider;
12
13 import java.util.*;
14
15 import org.eclipse.core.resources.*;
16 import org.eclipse.core.resources.mapping.*;
17 import org.eclipse.core.runtime.*;
18 import org.eclipse.core.runtime.jobs.ISchedulingRule;
19 import org.eclipse.core.runtime.jobs.MultiRule;
20 import org.eclipse.team.core.mapping.ISynchronizationScope;
21 import org.eclipse.team.core.mapping.ISynchronizationScopeManager;
22 import org.eclipse.team.core.subscribers.SubscriberScopeManager;
23 import org.eclipse.team.internal.core.Policy;
24 import org.eclipse.team.internal.core.mapping.*;
25
26 /**
27  * Class for translating a set of <code>ResourceMapping</code> objects
28  * representing a view selection into the complete set of resources to be
29  * operated on.
30  * <p>
31  * Here's a summary of the scope generation algorithm:
32  * <ol>
33  * <li>Obtain selected mappings
34  * <li>Project mappings onto resources using the appropriate context(s) in
35  * order to obtain a set of ResourceTraverals
36  * <li>Determine what model providers are interested in the targeted resources
37  * <li>From those model providers, obtain the set of affected resource mappings
38  * <li>If the original set is the same as the new set, we are done.
39  * <li>if the set differs from the original selection, rerun the mapping
40  * process for any new mappings
41  * <ul>
42  * <li>Only need to query model providers for mappings for new resources
43  * <li>keep repeating until no new mappings or resources are added
44  * </ul>
45  * </ol>
46  * <p>
47  * This implementation does not involve participants in the scope management
48  * process. It is up to subclasses that wish to support a longer life cycle for
49  * scopes to provide for participation. For example, the
50  * {@link SubscriberScopeManager} class includes participates in the scope
51  * management process.
52  * <p>
53  * This class is can be subclasses by clients.
54  *
55  * @see org.eclipse.core.resources.mapping.ResourceMapping
56  * @see SubscriberScopeManager
57  *
58  * @since 3.2
59  */

60 public class SynchronizationScopeManager extends PlatformObject implements ISynchronizationScopeManager {
61
62     private static final int MAX_ITERATION = 10;
63     private final ResourceMappingContext context;
64     private final boolean consultModels;
65     private ISynchronizationScope scope;
66     private boolean initialized;
67     private ScopeManagerEventHandler handler;
68     private final String JavaDoc name;
69
70     /**
71      * Convenience method for obtaining the set of resource
72      * mappings from all model providers that overlap
73      * with the given resources.
74      * @param traversals the resource traversals
75      * @param context the resource mapping context
76      * @param monitor a progress monitor
77      * @return the resource mappings
78      * @throws CoreException
79      */

80     public static ResourceMapping[] getMappingsFromProviders(ResourceTraversal[] traversals,
81             ResourceMappingContext context,
82             IProgressMonitor monitor) throws CoreException {
83         Set result = new HashSet();
84         IModelProviderDescriptor[] descriptors = ModelProvider
85                 .getModelProviderDescriptors();
86         for (int i = 0; i < descriptors.length; i++) {
87             IModelProviderDescriptor descriptor = descriptors[i];
88             ResourceMapping[] mappings = getMappings(descriptor, traversals,
89                     context, monitor);
90             result.addAll(Arrays.asList(mappings));
91             Policy.checkCanceled(monitor);
92         }
93         return (ResourceMapping[]) result.toArray(new ResourceMapping[result.size()]);
94     }
95     
96     private static ResourceMapping[] getMappings(IModelProviderDescriptor descriptor,
97             ResourceTraversal[] traversals,
98             ResourceMappingContext context, IProgressMonitor monitor)
99             throws CoreException {
100         ResourceTraversal[] matchingTraversals = descriptor.getMatchingTraversals(
101                 traversals);
102         return descriptor.getModelProvider().getMappings(matchingTraversals,
103                 context, monitor);
104     }
105     
106     /**
107      * Create a scope manager that uses the given context to
108      * determine what resources should be included in the scope.
109      * If <code>consultModels</code> is <code>true</code> then
110      * the model providers will be queried in order to determine if
111      * additional mappings should be included in the scope
112      * @param name the name of the scope
113      * @param inputMappings the input mappings
114      * @param resourceMappingContext a resource mapping context
115      * @param consultModels whether model providers should be consulted
116      */

117     public SynchronizationScopeManager(String JavaDoc name, ResourceMapping[] inputMappings, ResourceMappingContext resourceMappingContext, boolean consultModels) {
118         this.name = name;
119         this.context = resourceMappingContext;
120         this.consultModels = consultModels;
121         scope = createScope(inputMappings);
122     }
123
124     /* (non-Javadoc)
125      * @see org.eclipse.team.core.mapping.IResourceMappingScopeManager#isInitialized()
126      */

127     public boolean isInitialized() {
128         return initialized;
129     }
130     
131     /**
132      * Return the scheduling rule that is used when initializing and refreshing
133      * the scope. By default, a rule that covers all projects for the input mappings
134      * of the scope is returned. Subclasses may override.
135      *
136      * @return the scheduling rule that is used when initializing and refreshing
137      * the scope
138      */

139     public ISchedulingRule getSchedulingRule() {
140         Set projects = new HashSet();
141         ResourceMapping[] mappings = scope.getInputMappings();
142         for (int i = 0; i < mappings.length; i++) {
143             ResourceMapping mapping = mappings[i];
144             Object JavaDoc modelObject = mapping.getModelObject();
145             if (modelObject instanceof IResource) {
146                 IResource resource = (IResource) modelObject;
147                 if (resource.getType() == IResource.ROOT)
148                     // If the workspace root is one of the inputs,
149
// then use the workspace root as the rule
150
return ResourcesPlugin.getWorkspace().getRoot();
151                 projects.add(resource.getProject());
152             } else {
153                 // If one of the inputs is not a resource, then use the
154
// root as the rule since we don't know whether projects
155
// can be added or removed
156
return ResourcesPlugin.getWorkspace().getRoot();
157             }
158         }
159         return MultiRule.combine((IProject[]) projects.toArray(new IProject[projects.size()]));
160     }
161     
162     /* (non-Javadoc)
163      * @see org.eclipse.team.core.mapping.IResourceMappingScopeManager#initialize(org.eclipse.core.runtime.IProgressMonitor)
164      */

165     public void initialize(
166             IProgressMonitor monitor) throws CoreException {
167         ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
168             public void run(IProgressMonitor monitor) throws CoreException {
169                 internalPrepareContext(monitor);
170             }
171         }, getSchedulingRule(), IResource.NONE, monitor);
172     }
173
174     /* (non-Javadoc)
175      * @see org.eclipse.team.core.mapping.IResourceMappingScopeManager#refresh(org.eclipse.core.resources.mapping.ResourceMapping[], org.eclipse.core.runtime.IProgressMonitor)
176      */

177     public ResourceTraversal[] refresh(final ResourceMapping[] mappings, IProgressMonitor monitor) throws CoreException {
178         // We need to lock the workspace when building the scope
179
final ResourceTraversal[][] traversals = new ResourceTraversal[][] { new ResourceTraversal[0] };
180         IWorkspace workspace = ResourcesPlugin.getWorkspace();
181         workspace.run(new IWorkspaceRunnable() {
182             public void run(IProgressMonitor monitor) throws CoreException {
183                 traversals[0] = internalRefreshScope(mappings, true, monitor);
184             }
185         }, getSchedulingRule(), IResource.NONE, monitor);
186         return traversals[0];
187     }
188     
189     private void internalPrepareContext(IProgressMonitor monitor) throws CoreException {
190         if (initialized)
191             return;
192         monitor.beginTask(null, IProgressMonitor.UNKNOWN);
193         // Accumulate the initial set of mappings we need traversals for
194
((ResourceMappingScope)scope).reset();
195         ResourceMapping[] targetMappings = scope.getInputMappings();
196         ResourceTraversal[] newTraversals;
197         boolean firstTime = true;
198         boolean hasAdditionalResources = false;
199         int count = 0;
200         do {
201             Policy.checkCanceled(monitor);
202             newTraversals = addMappingsToScope(targetMappings,
203                     Policy.subMonitorFor(monitor, IProgressMonitor.UNKNOWN));
204             if (newTraversals.length > 0 && consultModels) {
205                 ResourceTraversal[] adjusted = adjustInputTraversals(newTraversals);
206                 targetMappings = getMappingsFromProviders(adjusted,
207                         context,
208                         Policy.subMonitorFor(monitor, IProgressMonitor.UNKNOWN));
209                 if (firstTime) {
210                     firstTime = false;
211                 } else if (!hasAdditionalResources) {
212                     hasAdditionalResources = newTraversals.length != 0;
213                 }
214             }
215         } while (consultModels & newTraversals.length != 0 && count++ < MAX_ITERATION);
216         setHasAdditionalMappings(scope, consultModels && internalHasAdditionalMappings());
217         setHasAdditionalResources(consultModels && hasAdditionalResources);
218         monitor.done();
219         initialized = true;
220         fireMappingsChangedEvent(scope.getMappings(), scope.getTraversals());
221     }
222
223     private ResourceTraversal[] internalRefreshScope(ResourceMapping[] mappings, boolean checkForContraction, IProgressMonitor monitor) throws CoreException {
224         monitor.beginTask(null, 100 * mappings.length + 100);
225         ScopeChangeEvent change = new ScopeChangeEvent(scope);
226         CompoundResourceTraversal refreshTraversals = new CompoundResourceTraversal();
227         CompoundResourceTraversal removedTraversals = new CompoundResourceTraversal();
228         for (int i = 0; i < mappings.length; i++) {
229             ResourceMapping mapping = mappings[i];
230             ResourceTraversal[] previousTraversals = scope.getTraversals(mapping);
231             ResourceTraversal[] mappingTraversals = mapping.getTraversals(
232                     context, Policy.subMonitorFor(monitor, 100));
233             refreshTraversals.addTraversals(mappingTraversals);
234             ResourceTraversal[] uncovered = getUncoveredTraversals(mappingTraversals);
235             if (checkForContraction && previousTraversals != null && previousTraversals.length > 0) {
236                 ResourceTraversal[] removed = getUncoveredTraversals(mappingTraversals, previousTraversals);
237                 removedTraversals.addTraversals(removed);
238             }
239             if (uncovered.length > 0) {
240                 change.setExpanded(true);
241                 ResourceTraversal[] result = performExpandScope(mapping, mappingTraversals, uncovered, monitor);
242                 refreshTraversals.addTraversals(result);
243             }
244         }
245         
246         if (checkForContraction && removedTraversals.getRoots().length > 0) {
247             // The scope may have contracted. The only way to handle this is to recalculate from scratch
248
// TODO: This may not be thread safe
249
((ResourceMappingScope)scope).reset();
250             internalRefreshScope(scope.getInputMappings(), false, monitor);
251             change.setContracted(true);
252         }
253         
254         if (change.shouldFireChange())
255             fireMappingsChangedEvent(change.getChangedMappings(), change.getChangedTraversals(refreshTraversals));
256         monitor.done();
257         return refreshTraversals.asTraversals();
258     }
259
260     private ResourceTraversal[] getUncoveredTraversals(
261             ResourceTraversal[] newTraversals,
262             ResourceTraversal[] previousTraversals) {
263         CompoundResourceTraversal t = new CompoundResourceTraversal();
264         t.addTraversals(newTraversals);
265         return t.getUncoveredTraversals(previousTraversals);
266     }
267
268     private ResourceTraversal[] performExpandScope(
269             ResourceMapping mapping, ResourceTraversal[] mappingTraversals,
270             ResourceTraversal[] uncovered, IProgressMonitor monitor)
271             throws CoreException {
272         ResourceMapping ancestor = findAncestor(mapping);
273         if (ancestor == null) {
274             uncovered = addMappingToScope(mapping, mappingTraversals);
275             addResourcesToScope(uncovered, monitor);
276             return mappingTraversals;
277         } else {
278             ResourceTraversal[] ancestorTraversals = ancestor.getTraversals(
279                     context, Policy.subMonitorFor(monitor, 100));
280             uncovered = addMappingToScope(ancestor, ancestorTraversals);
281             addResourcesToScope(uncovered, monitor);
282             return ancestorTraversals;
283         }
284     }
285
286     private ResourceMapping findAncestor(ResourceMapping mapping) {
287         ResourceMapping[] mappings = scope.getMappings(mapping.getModelProviderId());
288         for (int i = 0; i < mappings.length; i++) {
289             ResourceMapping m = mappings[i];
290             if (m.contains(mapping)) {
291                 return m;
292             }
293         }
294         return null;
295     }
296
297     private ResourceTraversal[] getUncoveredTraversals(ResourceTraversal[] traversals) {
298         return ((ResourceMappingScope)scope).getCompoundTraversal().getUncoveredTraversals(traversals);
299     }
300
301     private void addResourcesToScope(ResourceTraversal[] newTraversals, IProgressMonitor monitor) throws CoreException {
302         if (!consultModels)
303             return;
304         ResourceMapping[] targetMappings;
305         int count = 0;
306         do {
307             ResourceTraversal[] adjusted = adjustInputTraversals(newTraversals);
308             targetMappings = getMappingsFromProviders(adjusted,
309                     context, Policy.subMonitorFor(monitor, IProgressMonitor.UNKNOWN));
310             newTraversals = addMappingsToScope(targetMappings,
311                     Policy.subMonitorFor(monitor, IProgressMonitor.UNKNOWN));
312         } while (newTraversals.length != 0 && count++ < MAX_ITERATION);
313         if (!scope.hasAdditionalMappings()) {
314             setHasAdditionalMappings(scope, internalHasAdditionalMappings());
315         }
316         if (!scope.hasAdditonalResources()) {
317             setHasAdditionalResources(true);
318         }
319     }
320
321     /*
322      * Fire a mappings changed event to any listeners on the scope.
323      * The new mappings are obtained from the scope.
324      * @param originalMappings the original mappings of the scope.
325      */

326     private void fireMappingsChangedEvent(ResourceMapping[] newMappings, ResourceTraversal[] newTraversals) {
327         ((ResourceMappingScope)scope).fireTraversalsChangedEvent(newTraversals, newMappings);
328     }
329
330     /**
331      * set whether the scope has additional mappings. This method is not
332      * intended to be overridden.
333      *
334      * @param hasAdditionalMappings a boolean indicating if the scope has
335      * additional mappings
336      */

337     protected final void setHasAdditionalMappings(
338             ISynchronizationScope scope, boolean hasAdditionalMappings) {
339         ((ResourceMappingScope)scope).setHasAdditionalMappings(hasAdditionalMappings);
340     }
341
342     /**
343      * set whether the scope has additional resources. This method is not
344      * intended to be overridden.
345      *
346      * @param hasAdditionalResources a boolean indicating if the scope has
347      * additional resources
348      */

349     protected final void setHasAdditionalResources(boolean hasAdditionalResources) {
350         ((ResourceMappingScope)scope).setHasAdditionalResources(hasAdditionalResources);
351     }
352     
353     /**
354      * Create the scope that will be populated and returned by the builder. This
355      * method is not intended to be overridden by clients.
356      * @param inputMappings the input mappings
357      * @return a newly created scope that will be populated and returned by the
358      * builder
359      */

360     protected final ISynchronizationScope createScope(
361             ResourceMapping[] inputMappings) {
362         return new ResourceMappingScope(inputMappings, this);
363     }
364
365     /**
366      * Adjust the given set of input resources to include any additional
367      * resources required by a particular repository provider for the current
368      * operation. By default the original set is returned but subclasses may
369      * override. Overriding methods should return a set of resources that
370      * include the original resource either explicitly or implicitly as a child
371      * of a returned resource.
372      * <p>
373      * Subclasses may override this method to include additional resources
374      *
375      * @param traversals the input resource traversals
376      * @return the input resource traversals adjusted to include any additional resources
377      * required for the current operation
378      */

379     protected ResourceTraversal[] adjustInputTraversals(ResourceTraversal[] traversals) {
380         return traversals;
381     }
382
383     private ResourceTraversal[] addMappingsToScope(
384             ResourceMapping[] targetMappings,
385             IProgressMonitor monitor) throws CoreException {
386         CompoundResourceTraversal result = new CompoundResourceTraversal();
387         ResourceMappingContext context = this.context;
388         for (int i = 0; i < targetMappings.length; i++) {
389             ResourceMapping mapping = targetMappings[i];
390             if (scope.getTraversals(mapping) == null) {
391                 ResourceTraversal[] traversals = mapping.getTraversals(context,
392                         Policy.subMonitorFor(monitor, 100));
393                 ResourceTraversal[] newOnes = addMappingToScope(mapping, traversals);
394                 result.addTraversals(newOnes);
395             }
396             Policy.checkCanceled(monitor);
397         }
398         return result.asTraversals();
399     }
400
401     /**
402      * Add the mapping and its calculated traversals to the scope. Return the
403      * resources that were not previously covered by the scope. This method
404      * is not intended to be subclassed by clients.
405      *
406      * @param mapping the resource mapping
407      * @param traversals the resource mapping's traversals
408      * @return the resource traversals that were not previously covered by the scope
409      */

410     protected final ResourceTraversal[] addMappingToScope(
411             ResourceMapping mapping, ResourceTraversal[] traversals) {
412         return ((ResourceMappingScope)scope).addMapping(mapping, traversals);
413     }
414
415     private boolean internalHasAdditionalMappings() {
416         ResourceMapping[] inputMappings = scope.getInputMappings();
417         ResourceMapping[] mappings = scope.getMappings();
418         if (inputMappings.length == mappings.length) {
419             Set testSet = new HashSet();
420             for (int i = 0; i < mappings.length; i++) {
421                 ResourceMapping mapping = mappings[i];
422                 testSet.add(mapping);
423             }
424             for (int i = 0; i < inputMappings.length; i++) {
425                 ResourceMapping mapping = inputMappings[i];
426                 if (!testSet.contains(mapping)) {
427                     return true;
428                 }
429             }
430             return false;
431         }
432         return true;
433     }
434
435     /* (non-Javadoc)
436      * @see org.eclipse.team.core.mapping.IResourceMappingScopeManager#getContext()
437      */

438     public ResourceMappingContext getContext() {
439         return context;
440     }
441
442     /* (non-Javadoc)
443      * @see org.eclipse.team.core.mapping.IResourceMappingScopeManager#getScope()
444      */

445     public ISynchronizationScope getScope() {
446         return scope;
447     }
448     
449     /* (non-Javadoc)
450      * @see org.eclipse.team.core.mapping.IResourceMappingScopeManager#dispose()
451      */

452     public void dispose() {
453         if (handler != null)
454             handler.shutdown();
455     }
456
457     /**
458      * Refresh the given mappings by recalculating the traversals for the
459      * mappings and adjusting the scope accordingly.
460      * @param mappings the mappings to be refreshed
461      */

462     public void refresh(ResourceMapping[] mappings) {
463         getHandler().refresh(mappings);
464     }
465
466     private synchronized ScopeManagerEventHandler getHandler() {
467         if (handler == null)
468             handler = new ScopeManagerEventHandler(this);
469         return handler;
470     }
471
472     /**
473      * Returns the human readable name of this manager. The name is never
474      * <code>null</code>.
475      * @return the name associated with this scope manager
476      */

477     public String JavaDoc getName() {
478         return name;
479     }
480 }
481
Popular Tags