KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > ui > synchronize > ModelOperation


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.ui.synchronize;
12
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14 import java.util.ArrayList JavaDoc;
15 import java.util.Arrays JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.List JavaDoc;
18 import java.util.Set JavaDoc;
19
20 import org.eclipse.core.resources.IResource;
21 import org.eclipse.core.resources.mapping.*;
22 import org.eclipse.core.runtime.*;
23 import org.eclipse.jface.window.Window;
24 import org.eclipse.swt.widgets.Display;
25 import org.eclipse.team.core.mapping.*;
26 import org.eclipse.team.internal.core.Policy;
27 import org.eclipse.team.internal.core.mapping.CompoundResourceTraversal;
28 import org.eclipse.team.internal.ui.TeamUIMessages;
29 import org.eclipse.team.internal.ui.dialogs.AdditionalMappingsDialog;
30 import org.eclipse.team.ui.TeamOperation;
31 import org.eclipse.ui.IWorkbenchPart;
32
33 /**
34  * An abstract operation that uses an {@link ISynchronizationScopeManager} to
35  * create an operation scope that includes the complete set of mappings that
36  * must be included in the operation to ensure model consistency. The scope
37  * generation phase will prompt the user if additional resources have been added
38  * to the scope.
39  *
40  * @since 3.2
41  */

42 public abstract class ModelOperation extends TeamOperation {
43     
44     private boolean previewRequested;
45     private ISynchronizationScopeManager manager;
46     
47     /**
48      * Return the list of provides sorted by their extends relationship.
49      * Extended model providers will appear later in the list then those
50      * that extends them. The order of model providers that independant
51      * (i.e. no extends relationship between them) will be indeterminate.
52      * @param providers the model providers
53      * @return the list of provides sorted by their extends relationship
54      */

55     public static ModelProvider[] sortByExtension(ModelProvider[] providers) {
56         List JavaDoc result = new ArrayList JavaDoc();
57         for (int i = 0; i < providers.length; i++) {
58             ModelProvider providerToInsert = providers[i];
59             int index = result.size();
60             for (int j = 0; j < result.size(); j++) {
61                 ModelProvider provider = (ModelProvider) result.get(j);
62                 if (extendsProvider(providerToInsert, provider)) {
63                     index = j;
64                     break;
65                 }
66             }
67             result.add(index, providerToInsert);
68         }
69         return (ModelProvider[]) result.toArray(new ModelProvider[result.size()]);
70     }
71
72     private static boolean extendsProvider(ModelProvider providerToInsert, ModelProvider provider) {
73         String JavaDoc[] extended = providerToInsert.getDescriptor().getExtendedModels();
74         // First search immediate dependents
75
for (int i = 0; i < extended.length; i++) {
76             String JavaDoc id = extended[i];
77             if (id.equals(provider.getDescriptor().getId())) {
78                 return true;
79             }
80         }
81         return false;
82     }
83     
84     /**
85      * Create a model operation that operates on the given scope.
86      * @param part the workbench part from which the merge was launched or <code>null</code>
87      * @param manager the scope manager for this operation
88      */

89     protected ModelOperation(IWorkbenchPart part, ISynchronizationScopeManager manager) {
90         super(part);
91         this.manager = manager;
92     }
93     
94     /**
95      * Run the operation. This method first ensures that the scope is built
96      * by calling {@link #initializeScope(IProgressMonitor)} and then invokes the
97      * {@link #execute(IProgressMonitor)} method.
98      * @param monitor a progress monitor
99      * @see org.eclipse.jface.operation.IRunnableWithProgress#run(org.eclipse.core.runtime.IProgressMonitor)
100      */

101     public final void run(IProgressMonitor monitor) throws InvocationTargetException JavaDoc,
102             InterruptedException JavaDoc {
103         try {
104             monitor.beginTask(null, 100);
105             beginOperation(Policy.subMonitorFor(monitor, 5));
106             execute(Policy.subMonitorFor(monitor, 90));
107         } finally {
108             endOperation(Policy.subMonitorFor(monitor, 5));
109             monitor.done();
110         }
111     }
112     
113     /**
114      * Method called from {@link #run(IProgressMonitor)} before
115      * the {@link #execute(IProgressMonitor)} method is invoked.
116      * This is done to give the operation a chance to initialize
117      * any state required to execute. By default, the
118      * {@link ISynchronizationScopeManager} for this operation
119      * is initialized if it was not previously initialized.
120      * @param monitor a progress monitor
121      * @throws InvocationTargetException
122      */

123     protected void beginOperation(IProgressMonitor monitor) throws InvocationTargetException JavaDoc {
124         initializeScope(monitor);
125     }
126
127     /**
128      * Method called from {@link #run(IProgressMonitor)} after the
129      * {@link #execute(IProgressMonitor)} completes of if an exception
130      * is thrown from the {@link #beginOperation(IProgressMonitor)}
131      * or the {@link #execute(IProgressMonitor)}. By default,
132      * this method does nothing. Subclasses may override.
133      * @param monitor a progress monitor
134      */

135     protected void endOperation(IProgressMonitor monitor) throws InvocationTargetException JavaDoc {
136         // Do nothing by deafult
137
}
138
139     /**
140      * Adjust the input of the operation according to the selected
141      * resource mappings and the set of interested participants. This method
142      * will prompt the user in the following cases:
143      * <ol>
144      * <li>The scope contains additional resources than those in the input.
145      * <li>The scope has additional mappings from a model in the input
146      * <li>The input contains elements from multiple models
147      * </ol>
148      * <p>
149      * The scope of this operation will only be prepared once. Subsequent
150      * calls to this method will do nothing. Also, if the scope was provided
151      * as an argument to a constructor, this method will do nothing (i.e. the
152      * scope will not be prepared again and no prompting will occur).
153      * <p>
154      * Subclasses can customize how the scope is generated by overriding
155      * the {@link #getScopeManager()} to return a custom scope manager.
156      * @param monitor a progress monitor
157      */

158     protected final void initializeScope(IProgressMonitor monitor) throws InvocationTargetException JavaDoc {
159         try {
160             if (!manager.isInitialized()) {
161                 manager.initialize(monitor);
162                 promptIfInputChange(monitor);
163             }
164         } catch (CoreException e) {
165             throw new InvocationTargetException JavaDoc(e);
166         }
167     }
168
169     /**
170      * Prompt the user by calling {@link #promptForInputChange(String, IProgressMonitor)}
171      * if the scope of the operation was expanded (as described in
172      * {@link #initializeScope(IProgressMonitor)}).
173      * @param monitor a progress monitor
174      */

175     protected void promptIfInputChange(IProgressMonitor monitor) {
176         ISynchronizationScope inputScope = getScope().asInputScope();
177         if (getScope().hasAdditionalMappings()) {
178             boolean prompt = false;
179             // There are additional mappings so we may need to prompt
180
ModelProvider[] inputModelProviders = inputScope.getModelProviders();
181             if (hasAdditionalMappingsFromIndependantModel(inputModelProviders, getScope().getModelProviders())) {
182                 // Prompt if the is a new model provider in the scope that is independant
183
// of any of the input mappings
184
prompt = true;
185             } else if (getScope().hasAdditonalResources()) {
186                 // We definitely need to prompt to indicate that additional resources
187
prompt = true;
188             } else if (inputModelProviders.length == 1) {
189                 // We may need to prompt depending on the nature of the additional mappings
190
// We need to prompt if the additional mappings are from the same model as
191
// the input or if they are from a model that has no relationship to the input model
192
String JavaDoc modelProviderId = inputModelProviders[0].getDescriptor().getId();
193                 ResourceMapping[] mappings = getScope().getMappings();
194                 for (int i = 0; i < mappings.length; i++) {
195                     ResourceMapping mapping = mappings[i];
196                     if (inputScope.getTraversals(mapping) == null) {
197                         // This mapping was not in the input
198
String JavaDoc id = mapping.getModelProviderId();
199                         if (id.equals(modelProviderId) && !modelProviderId.equals(ModelProvider.RESOURCE_MODEL_PROVIDER_ID)) {
200                             prompt = true;
201                             break;
202                         } else if (isIndependantModel(modelProviderId, id)) {
203                             prompt = true;
204                             break;
205                         }
206                     }
207                 }
208             } else {
209                 // We need to prompt if there are additional mappings from an input
210
// provider whose traversals overlap those of the input mappings.
211
for (int i = 0; i < inputModelProviders.length; i++) {
212                     ModelProvider provider = inputModelProviders[i];
213                     String JavaDoc id = provider.getDescriptor().getId();
214                     ResourceMapping[] inputMappings = inputScope.getMappings(id);
215                     ResourceMapping[] scopeMappings = getScope().getMappings(id);
216                     if (inputMappings.length != scopeMappings.length) {
217                         // There are more mappings for this provider.
218
// We need to see if any of the new ones overlap the old ones.
219
for (int j = 0; j < scopeMappings.length; j++) {
220                             ResourceMapping mapping = scopeMappings[j];
221                             ResourceTraversal[] inputTraversals = inputScope.getTraversals(mapping);
222                             if (inputTraversals == null) {
223                                 // This mapping was not in the input.
224
// We need to prompt if the traversal for this mapping overlaps with
225
// the input mappings for the model provider
226
// TODO could check for project overlap first
227
ResourceTraversal[] scopeTraversals = getScope().getTraversals(mapping);
228                                 ResourceTraversal[] inputModelTraversals = getTraversals(inputScope, inputMappings);
229                                 if (overlaps(scopeTraversals, inputModelTraversals)) {
230                                     prompt = true;
231                                     break;
232                                 }
233                             }
234                         }
235                     }
236                 }
237             }
238             if (prompt) {
239                 String JavaDoc previewMessage = getPreviewRequestMessage();
240                 previewRequested = promptForInputChange(previewMessage, monitor);
241             }
242         }
243     }
244
245     /**
246      * Return a string to be used in the preview request on the scope prompt
247      * or <code>null</code> if a preview of the operation results is not possible.
248      * By default, <code>null</code> is returned but subclasses may override.
249      * @return a string to be used in the preview request on the scope prompt
250      * or <code>null</code> if a preview of the operation results is not possible
251      */

252     protected String JavaDoc getPreviewRequestMessage() {
253         return null;
254     }
255
256     private boolean hasAdditionalMappingsFromIndependantModel(ModelProvider[] inputModelProviders, ModelProvider[] modelProviders) {
257         ModelProvider[] additionalProviders = getAdditionalProviders(inputModelProviders, modelProviders);
258         for (int i = 0; i < additionalProviders.length; i++) {
259             ModelProvider additionalProvider = additionalProviders[i];
260             boolean independant = true;
261             // Return true if the new provider is independant of all input providers
262
for (int j = 0; j < inputModelProviders.length; j++) {
263                 ModelProvider inputProvider = inputModelProviders[j];
264                 if (!isIndependantModel(additionalProvider.getDescriptor().getId(), inputProvider.getDescriptor().getId())) {
265                     independant = false;
266                 }
267             }
268             if (independant)
269                 return true;
270         }
271         return false;
272     }
273
274     private ModelProvider[] getAdditionalProviders(ModelProvider[] inputModelProviders, ModelProvider[] modelProviders) {
275         Set JavaDoc input = new HashSet JavaDoc();
276         List JavaDoc result = new ArrayList JavaDoc();
277         input.addAll(Arrays.asList(inputModelProviders));
278         for (int i = 0; i < modelProviders.length; i++) {
279             ModelProvider provider = modelProviders[i];
280             if (!input.contains(provider))
281                 result.add(provider);
282         }
283         return (ModelProvider[]) result.toArray(new ModelProvider[result.size()]);
284     }
285
286     private boolean overlaps(ResourceTraversal[] scopeTraversals, ResourceTraversal[] inputModelTraversals) {
287         for (int i = 0; i < inputModelTraversals.length; i++) {
288             ResourceTraversal inputTraversal = inputModelTraversals[i];
289             for (int j = 0; j < scopeTraversals.length; j++) {
290                 ResourceTraversal scopeTraversal = scopeTraversals[j];
291                 if (overlaps(inputTraversal, scopeTraversal)) {
292                     return true;
293                 }
294             }
295         }
296         return false;
297     }
298
299     private boolean overlaps(ResourceTraversal inputTraversal, ResourceTraversal scopeTraversal) {
300         IResource[] inputRoots = inputTraversal.getResources();
301         IResource[] scopeRoots = scopeTraversal.getResources();
302         for (int i = 0; i < scopeRoots.length; i++) {
303             IResource scopeResource = scopeRoots[i];
304             for (int j = 0; j < inputRoots.length; j++) {
305                 IResource inputResource = inputRoots[j];
306                 if (overlaps(scopeResource, scopeTraversal.getDepth(), inputResource, inputTraversal.getDepth()))
307                     return true;
308             }
309         }
310         return false;
311     }
312
313     private boolean overlaps(IResource scopeResource, int scopeDepth, IResource inputResource, int inputDepth) {
314         if (scopeResource.equals(inputResource))
315             return true;
316         if (scopeDepth == IResource.DEPTH_INFINITE && scopeResource.getFullPath().isPrefixOf(inputResource.getFullPath())) {
317             return true;
318         }
319         if (scopeDepth == IResource.DEPTH_ONE && scopeResource.equals(inputResource.getParent())) {
320             return true;
321         }
322         if (inputDepth == IResource.DEPTH_INFINITE && inputResource.getFullPath().isPrefixOf(scopeResource.getFullPath())) {
323             return true;
324         }
325         if (inputDepth == IResource.DEPTH_ONE && inputResource.equals(scopeResource.getParent())) {
326             return true;
327         }
328         return false;
329     }
330
331     private ResourceTraversal[] getTraversals(ISynchronizationScope inputScope, ResourceMapping[] inputMappings) {
332         CompoundResourceTraversal result = new CompoundResourceTraversal();
333         for (int i = 0; i < inputMappings.length; i++) {
334             ResourceMapping mapping = inputMappings[i];
335             result.addTraversals(inputScope.getTraversals(mapping));
336         }
337         return result.asTraversals();
338     }
339
340     private boolean isIndependantModel(String JavaDoc modelProviderId, String JavaDoc id) {
341         if (id.equals(modelProviderId))
342             return false;
343         IModelProviderDescriptor desc1 = ModelProvider.getModelProviderDescriptor(modelProviderId);
344         IModelProviderDescriptor desc2 = ModelProvider.getModelProviderDescriptor(id);
345         return !(isExtension(desc1, desc2) || isExtension(desc2, desc1));
346     }
347
348     /*
349      * Return whether the desc1 model extends the desc2 model
350      */

351     private boolean isExtension(IModelProviderDescriptor desc1, IModelProviderDescriptor desc2) {
352         String JavaDoc[] ids = desc1.getExtendedModels();
353         // First check direct extension
354
for (int i = 0; i < ids.length; i++) {
355             String JavaDoc id = ids[i];
356             if (id.equals(desc2.getId())) {
357                 return true;
358             }
359         }
360         // Now check for indirect extension
361
for (int i = 0; i < ids.length; i++) {
362             String JavaDoc id = ids[i];
363             IModelProviderDescriptor desc3 = ModelProvider.getModelProviderDescriptor(id);
364             if (isExtension(desc3, desc2)) {
365                 return true;
366             }
367         }
368         return false;
369     }
370
371     /**
372      * Prompt the user to inform them that additional resource mappings
373      * have been included in the operations.
374      * @param requestPreviewMessage message to be displayed for the option to force a preview
375      * (or <code>null</code> if the preview option should not be presented
376      * @param monitor a progress monitor
377      * @return whether a preview of the operation results was requested
378      * @throws OperationCanceledException if the user choose to cancel
379      */

380     protected boolean promptForInputChange(String JavaDoc requestPreviewMessage, IProgressMonitor monitor) {
381         return showAllMappings(requestPreviewMessage);
382     }
383
384     private boolean showAllMappings(final String JavaDoc requestPreviewMessage) {
385         final boolean[] canceled = new boolean[] { false };
386         final boolean[] forcePreview = new boolean[] { false };
387         Display.getDefault().syncExec(new Runnable JavaDoc() {
388             public void run() {
389                 AdditionalMappingsDialog dialog = new AdditionalMappingsDialog(getShell(), TeamUIMessages.ResourceMappingOperation_0, getScope(), getContext());
390                 dialog.setPreviewMessage(requestPreviewMessage);
391                 int result = dialog.open();
392                 canceled[0] = result != Window.OK;
393                 if (requestPreviewMessage != null) {
394                     forcePreview[0] = dialog.isForcePreview();
395                 }
396             }
397         });
398         
399         if (canceled[0]) {
400             throw new OperationCanceledException();
401         }
402         return forcePreview[0];
403     }
404     
405     /**
406      * Return the synchronization context for the operation or <code>null</code>
407      * if the operation doesn't have one or if it has not yet been created.
408      * By default, the method always returns <code>null</code>. Subclasses may override.
409      * @return the synchronization context for the operation or <code>null</code>
410      */

411     protected ISynchronizationContext getContext() {
412         return null;
413     }
414
415     /**
416      * Execute the operation. This method is invoked after the
417      * scope has been generated.
418      * @param monitor a progress monitor
419      * @throws InvocationTargetException
420      * @throws InterruptedException
421      */

422     protected abstract void execute(IProgressMonitor monitor) throws InvocationTargetException JavaDoc,
423             InterruptedException JavaDoc;
424
425     /**
426      * Return the scope of this operation.
427      * @return the scope of this operation
428      */

429     public ISynchronizationScope getScope() {
430         return manager.getScope();
431     }
432
433     /**
434      * Return whether a preview of the operation before it is performed is
435      * desired.
436      * @return whether a preview of the operation before it is performed is
437      * desired
438      */

439     public boolean isPreviewRequested() {
440         return previewRequested;
441     }
442
443     /**
444      * Return the scope manager for this operation.
445      * @return the scope manager for this operation.
446      */

447     protected ISynchronizationScopeManager getScopeManager() {
448         return manager;
449     }
450     
451 }
452
Popular Tags