KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*******************************************************************************
2  * Copyright (c) 2006, 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.ui.synchronize;
12
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14 import java.util.ArrayList JavaDoc;
15 import java.util.List JavaDoc;
16
17 import org.eclipse.core.resources.*;
18 import org.eclipse.core.resources.mapping.ModelProvider;
19 import org.eclipse.core.runtime.*;
20 import org.eclipse.core.runtime.jobs.Job;
21 import org.eclipse.jface.dialogs.*;
22 import org.eclipse.swt.widgets.Composite;
23 import org.eclipse.swt.widgets.Display;
24 import org.eclipse.team.core.TeamException;
25 import org.eclipse.team.core.diff.*;
26 import org.eclipse.team.core.mapping.*;
27 import org.eclipse.team.internal.ui.*;
28 import org.eclipse.team.internal.ui.dialogs.NoChangesDialog;
29 import org.eclipse.ui.IWorkbenchPart;
30
31 /**
32  * A model operation that executes a merge according to the merge lifecycle
33  * associated with an {@link IMergeContext} and {@link IResourceMappingMerger}
34  * instances obtained from the model providers involved.
35  *
36  * @since 3.2
37  */

38 public abstract class ModelMergeOperation extends ModelOperation {
39
40     /**
41      * Validate the merge context with the model providers that have mappings in
42      * the scope of the context. The {@link IResourceMappingMerger} for each
43      * model provider will be consulted and any non-OK status will be
44      * accumulated and returned,
45      *
46      * @param context
47      * the merge context being validated
48      * @param monitor
49      * a progress monitor
50      * @return a status or multi-status that identify any conditions that should
51      * force a preview of the merge
52      */

53     public static IStatus validateMerge(IMergeContext context, IProgressMonitor monitor) {
54         try {
55             ModelProvider[] providers = context.getScope().getModelProviders();
56             monitor.beginTask(null, 100 * providers.length);
57             List JavaDoc notOK = new ArrayList JavaDoc();
58             for (int i = 0; i < providers.length; i++) {
59                 ModelProvider provider = providers[i];
60                 IStatus status = validateMerge(provider, context, Policy.subMonitorFor(monitor, 100));
61                 if (!status.isOK())
62                     notOK.add(status);
63             }
64             if (notOK.isEmpty())
65                 return Status.OK_STATUS;
66             if (notOK.size() == 1)
67                 return (IStatus)notOK.get(0);
68             return new MultiStatus(TeamUIPlugin.ID, 0, (IStatus[]) notOK.toArray(new IStatus[notOK.size()]), TeamUIMessages.ResourceMappingMergeOperation_3, null);
69         } finally {
70             monitor.done();
71         }
72     }
73     
74     /*
75      * Validate the merge by obtaining the {@link IResourceMappingMerger} for the
76      * given provider.
77      * @param provider the model provider
78      * @param context the merge context
79      * @param monitor a progress monitor
80      * @return the status obtained from the merger for the provider
81      */

82     private static IStatus validateMerge(ModelProvider provider, IMergeContext context, IProgressMonitor monitor) {
83         IResourceMappingMerger merger = getMerger(provider);
84         if (merger == null)
85             return Status.OK_STATUS;
86         return merger.validateMerge(context, monitor);
87     }
88     
89     /*
90      * Return the auto-merger associated with the given model provider using the
91      * adaptable mechanism. If the model provider does not have a merger
92      * associated with it, a default merger that performs the merge at the file
93      * level is returned.
94      *
95      * @param provider
96      * the model provider of the elements to be merged (must not be
97      * <code>null</code>)
98      * @return a merger
99      */

100     private static IResourceMappingMerger getMerger(ModelProvider provider) {
101         Assert.isNotNull(provider);
102         return (IResourceMappingMerger)Utils.getAdapter(provider, IResourceMappingMerger.class);
103     }
104     
105     /**
106      * Create a model merge operation.
107      * @param part the workbench part from which the operation was requested or <code>null</code>
108      * @param manager the scope manager
109      */

110     protected ModelMergeOperation(IWorkbenchPart part, ISynchronizationScopeManager manager) {
111         super(part, manager);
112     }
113     
114     /**
115      * Perform a merge. First {@link #initializeContext(IProgressMonitor)} is
116      * called to determine the set of resource changes. Then the
117      * {@link #executeMerge(IProgressMonitor)} method is invoked.
118      *
119      * @param monitor a progress monitor
120      */

121     protected void execute(IProgressMonitor monitor)
122             throws InvocationTargetException JavaDoc, InterruptedException JavaDoc {
123         try {
124             monitor.beginTask(null, 100);
125             initializeContext(Policy.subMonitorFor(monitor, 50));
126             executeMerge(Policy.subMonitorFor(monitor, 50));
127         } catch (CoreException e) {
128             throw new InvocationTargetException JavaDoc(e);
129         } finally {
130             monitor.done();
131         }
132     }
133
134     /**
135      * Perform a merge. This method is invoked from
136      * {@link #execute(IProgressMonitor)} after the context has been
137      * initialized. If there are changes in the context, they will be validating
138      * by calling {@link #validateMerge(IMergeContext, IProgressMonitor)}. If
139      * there are no validation problems, {@link #performMerge(IProgressMonitor)}
140      * will then be called to perform the merge. If there are problems encountered
141      * or if a preview was requested, {@link #handlePreviewRequest()} is called.
142      *
143      * @param monitor a progress monitor
144      */

145     protected void executeMerge(IProgressMonitor monitor) throws CoreException {
146         monitor.beginTask(null, 100);
147         if (!hasChangesOfInterest()) {
148             handleNoChanges();
149         } else if (isPreviewRequested()) {
150             handlePreviewRequest();
151         } else {
152             IStatus status = ModelMergeOperation.validateMerge(getMergeContext(), Policy.subMonitorFor(monitor, 10));
153             if (!status.isOK()) {
154                 handleValidationFailure(status);
155             } else {
156                 status = performMerge(Policy.subMonitorFor(monitor, 90));
157                 if (!status.isOK()) {
158                     handleMergeFailure(status);
159                 }
160             }
161         }
162         monitor.done();
163     }
164     
165     /**
166      * A preview of the merge has been requested. By default, this method does
167      * nothing. Subclasses that wish to support previewing must override this
168      * method to preview the merge and the {@link #getPreviewRequestMessage()}
169      * to have the option presented to the user if the scope changes.
170      */

171     protected void handlePreviewRequest() {
172         // Do nothing
173
}
174
175     /**
176      * Initialize the merge context for this merge operation.
177      * After this method is invoked, the {@link #getContext()}
178      * method must return an instance of {@link IMergeContext}
179      * that is fully initialized.
180      * @param monitor a progress monitor
181      * @throws CoreException
182      */

183     protected abstract void initializeContext(IProgressMonitor monitor) throws CoreException;
184
185     /**
186      * Method invoked when the context contains changes that failed validation
187      * by at least one {@link IResourceMappingMerger}.
188      * By default, the user is prompted to inform them that unmergeable changes were found
189      * and the {@link #handlePreviewRequest()} method is invoked.
190      * Subclasses may override.
191      * @param status the status returned from the mergers that reported the validation failures
192      */

193     protected void handleValidationFailure(final IStatus status) {
194         final boolean[] result = new boolean[] { false };
195         Runnable JavaDoc runnable = new Runnable JavaDoc() {
196             public void run() {
197                 ErrorDialog dialog = new ErrorDialog(getShell(), TeamUIMessages.ModelMergeOperation_0, TeamUIMessages.ModelMergeOperation_1, status, IStatus.ERROR | IStatus.WARNING | IStatus.INFO) {
198                     protected void createButtonsForButtonBar(Composite parent) {
199                         createButton(parent, IDialogConstants.YES_ID, IDialogConstants.YES_LABEL,
200                                 false);
201                         createButton(parent, IDialogConstants.NO_ID, IDialogConstants.NO_LABEL,
202                                 true);
203                         createDetailsButton(parent);
204                     }
205                     /* (non-Javadoc)
206                      * @see org.eclipse.jface.dialogs.ErrorDialog#buttonPressed(int)
207                      */

208                     protected void buttonPressed(int id) {
209                         if (id == IDialogConstants.YES_ID)
210                             super.buttonPressed(IDialogConstants.OK_ID);
211                         else if (id == IDialogConstants.NO_ID)
212                             super.buttonPressed(IDialogConstants.CANCEL_ID);
213                         super.buttonPressed(id);
214                     }
215                 };
216                 int code = dialog.open();
217                 result[0] = code == 0;
218             }
219         };
220         getShell().getDisplay().syncExec(runnable);
221         if (result[0])
222             handlePreviewRequest();
223     }
224
225     /**
226      * Method invoked when the context contains unmergable changes.
227      * By default, the user is prompted to inform them that unmergeable changes were found.
228      * Subclasses may override.
229      * @param status the status returned from the merger that reported the conflict
230      */

231     protected void handleMergeFailure(final IStatus status) {
232         Display.getDefault().syncExec(new Runnable JavaDoc() {
233             public void run() {
234                 MessageDialog.openInformation(getShell(), TeamUIMessages.MergeIncomingChangesAction_0, status.getMessage());
235             };
236         });
237         handlePreviewRequest();
238     }
239
240     /**
241      * Method invoked when the context contains no changes.
242      * By default, the user is prompted to inform them that no changes were found.
243      * Subclasses may override.
244      */

245     protected void handleNoChanges() {
246         Display.getDefault().syncExec(new Runnable JavaDoc() {
247             public void run() {
248                 NoChangesDialog.open(getShell(), TeamUIMessages.ResourceMappingMergeOperation_0, TeamUIMessages.ResourceMappingMergeOperation_1, TeamUIMessages.ModelMergeOperation_3, getScope().asInputScope());
249             };
250         });
251     }
252     
253     /**
254      * Attempt a headless merge of the elements in the context of this
255      * operation. The merge is performed by obtaining the
256      * {@link IResourceMappingMerger} for the model providers in the context's
257      * scope. The merger of the model providers are invoked in the order
258      * determined by the {@link ModelOperation#sortByExtension(ModelProvider[])}
259      * method. The method will stop on the first conflict encountered.
260      * This method will throw a runtime exception
261      * if the operation does not have a merge context.
262      *
263      * @param monitor
264      * a progress monitor
265      * @return a status that indicates whether the merge succeeded.
266      * @throws CoreException
267      * if an error occurred
268      */

269     protected IStatus performMerge(IProgressMonitor monitor) throws CoreException {
270         ISynchronizationContext sc = getContext();
271         if (sc instanceof IMergeContext) {
272             IMergeContext context = (IMergeContext) sc;
273             final ModelProvider[] providers = sortByExtension(context.getScope().getModelProviders());
274             final IStatus[] result = new IStatus[] { Status.OK_STATUS };
275             context.run(new IWorkspaceRunnable() {
276                 public void run(IProgressMonitor monitor) throws CoreException {
277                     try {
278                         int ticks = 100;
279                         monitor.beginTask(null, ticks + ((providers.length - 1) * 10));
280                         for (int i = 0; i < providers.length; i++) {
281                             ModelProvider provider = providers[i];
282                             IStatus status = performMerge(provider, Policy.subMonitorFor(monitor, ticks));
283                             ticks = 10;
284                             if (!status.isOK()) {
285                                 // Stop at the first failure
286
result[0] = status;
287                                 return;
288                             }
289                             try {
290                                 Job.getJobManager().join(getContext(), monitor);
291                             } catch (InterruptedException JavaDoc e) {
292                                 // Ignore
293
}
294                         }
295                     } finally {
296                         monitor.done();
297                     }
298                 }
299             }, null /* scheduling rule */, IResource.NONE, monitor);
300             return result[0];
301         }
302         return noMergeContextAvailable();
303     }
304     
305     /**
306      * Attempt to merge all the mappings that come from the given provider.
307      * Return a status which indicates whether the merge succeeded or if
308      * unmergeable conflicts were found. By default, this method invokes
309      * the {@link IResourceMappingMerger#merge(IMergeContext, IProgressMonitor)}
310      * method but does not wait for the context to update (see {@link ISynchronizationContext}.
311      * Callers that are invoking the merge on multiple models should wait until the
312      * context has updated before invoking merge on another merger. The following
313      * line of code will wait for the context to update:
314      * <pre>
315      * Platform.getJobManager().join(getContext(), monitor);
316      * </pre>
317      * <p>
318      * This method will throw a runtime exception
319      * if the operation does not have a merge context.
320      * @param provider the model provider whose mappings are to be merged
321      * @param monitor a progress monitor
322      * @return a non-OK status if there were unmergable conflicts
323      * @throws CoreException if an error occurred
324      */

325     protected IStatus performMerge(ModelProvider provider, IProgressMonitor monitor) throws CoreException {
326         ISynchronizationContext sc = getContext();
327         if (sc instanceof IMergeContext) {
328             IMergeContext context = (IMergeContext) sc;
329             IResourceMappingMerger merger = getMerger(provider);
330             if (merger != null) {
331                 IStatus status = merger.merge(context, monitor);
332                 if (status.isOK() || status.getCode() == IMergeStatus.CONFLICTS) {
333                     return status;
334                 }
335                 throw new TeamException(status);
336             }
337             return Status.OK_STATUS;
338         }
339         return noMergeContextAvailable();
340     }
341     
342     private IStatus noMergeContextAvailable() {
343         throw new IllegalStateException JavaDoc(TeamUIMessages.ModelMergeOperation_2);
344     }
345     
346     /**
347      * Return whether the context of this operation has changes that are
348      * of interest to the operation. Subclasses may override.
349      * @return whether the context of this operation has changes that are
350      * of interest to the operation
351      */

352     protected boolean hasChangesOfInterest() {
353         return !getContext().getDiffTree().isEmpty() && hasIncomingChanges(getContext().getDiffTree());
354     }
355     
356     private boolean hasIncomingChanges(IDiffTree tree) {
357         return tree.hasMatchingDiffs(ResourcesPlugin.getWorkspace().getRoot().getFullPath(), new FastDiffFilter() {
358             public boolean select(IDiff node) {
359                 if (node instanceof IThreeWayDiff) {
360                     IThreeWayDiff twd = (IThreeWayDiff) node;
361                     int direction = twd.getDirection();
362                     if (direction == IThreeWayDiff.INCOMING || direction == IThreeWayDiff.CONFLICTING) {
363                         return true;
364                     }
365                 } else {
366                     // Return true for any two-way change
367
return true;
368                 }
369                 return false;
370             }
371         });
372     }
373     
374     private IMergeContext getMergeContext() {
375         return (IMergeContext)getContext();
376     }
377 }
378
Popular Tags