KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ltk > ui > refactoring > model > AbstractResourceMappingMerger


1 /*******************************************************************************
2  * Copyright (c) 2005, 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.ltk.ui.refactoring.model;
12
13 import java.io.IOException JavaDoc;
14 import java.io.InputStream JavaDoc;
15 import java.util.HashSet JavaDoc;
16 import java.util.Set JavaDoc;
17
18 import org.eclipse.team.core.diff.IDiff;
19 import org.eclipse.team.core.diff.IThreeWayDiff;
20 import org.eclipse.team.core.diff.ITwoWayDiff;
21 import org.eclipse.team.core.history.IFileRevision;
22 import org.eclipse.team.core.mapping.IMergeContext;
23 import org.eclipse.team.core.mapping.IMergeStatus;
24 import org.eclipse.team.core.mapping.IResourceDiff;
25 import org.eclipse.team.core.mapping.ResourceMappingMerger;
26 import org.eclipse.team.core.mapping.provider.MergeStatus;
27
28 import org.eclipse.core.runtime.Assert;
29 import org.eclipse.core.runtime.CoreException;
30 import org.eclipse.core.runtime.IProgressMonitor;
31 import org.eclipse.core.runtime.IStatus;
32 import org.eclipse.core.runtime.NullProgressMonitor;
33 import org.eclipse.core.runtime.Status;
34 import org.eclipse.core.runtime.SubProgressMonitor;
35
36 import org.eclipse.core.resources.IProject;
37 import org.eclipse.core.resources.IStorage;
38 import org.eclipse.core.resources.IWorkspaceRoot;
39 import org.eclipse.core.resources.ResourcesPlugin;
40 import org.eclipse.core.resources.mapping.ModelProvider;
41 import org.eclipse.core.resources.mapping.ResourceMapping;
42
43 import org.eclipse.ltk.core.refactoring.RefactoringCore;
44 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
45 import org.eclipse.ltk.core.refactoring.RefactoringDescriptorProxy;
46 import org.eclipse.ltk.core.refactoring.history.RefactoringHistory;
47
48 import org.eclipse.ltk.internal.core.refactoring.history.RefactoringHistoryImplementation;
49 import org.eclipse.ltk.internal.core.refactoring.history.RefactoringHistoryService;
50 import org.eclipse.ltk.internal.ui.refactoring.RefactoringUIMessages;
51 import org.eclipse.ltk.internal.ui.refactoring.RefactoringUIPlugin;
52 import org.eclipse.ltk.internal.ui.refactoring.model.RefactoringHistoryMergeWizard;
53
54 import org.eclipse.swt.widgets.Display;
55 import org.eclipse.swt.widgets.Shell;
56
57 import org.eclipse.jface.dialogs.MessageDialog;
58 import org.eclipse.jface.window.Window;
59 import org.eclipse.jface.wizard.WizardDialog;
60
61 import org.eclipse.ui.IWorkbench;
62 import org.eclipse.ui.IWorkbenchWindow;
63 import org.eclipse.ui.PlatformUI;
64
65 import org.eclipse.ltk.ui.refactoring.history.RefactoringHistoryControlConfiguration;
66
67 /**
68  * Partial implementation of a refactoring-aware resource mapping merger.
69  * <p>
70  * This class provides support to determine pending refactorings during model
71  * merging and model update, and displays a refactoring wizard to apply the
72  * refactorings to the local workspace.
73  * </p>
74  * <p>
75  * Note: this class is designed to be extended by clients. Programming language
76  * implementers which need a refactoring-aware resource mapping merger to
77  * associated with their model provider may extend this class to implement
78  * language-specific project dependency rules.
79  * </p>
80  *
81  * @see org.eclipse.team.core.mapping.IResourceMappingMerger
82  *
83  * @since 3.2
84  */

85 public abstract class AbstractResourceMappingMerger extends ResourceMappingMerger {
86
87     /** Refactoring history model merge configuration */
88     private static final class RefactoringHistoryModelMergeConfiguration extends RefactoringHistoryControlConfiguration {
89
90         /**
91          * Creates a new refactoring history model merge configuration.
92          *
93          * @param project
94          * the project, or <code>null</code>
95          */

96         public RefactoringHistoryModelMergeConfiguration(final IProject project) {
97             super(project, false, false);
98         }
99
100         /**
101          * {@inheritDoc}
102          */

103         public String JavaDoc getProjectPattern() {
104             return RefactoringUIMessages.RefactoringModelMerger_project_pattern;
105         }
106
107         /**
108          * {@inheritDoc}
109          */

110         public String JavaDoc getWorkspaceCaption() {
111             return RefactoringUIMessages.RefactoringModelMerger_workspace_caption;
112         }
113     }
114
115     /** The refactoring history model merge wizard */
116     private static final class RefactoringHistoryModelMergeWizard extends RefactoringHistoryMergeWizard {
117
118         /**
119          * Creates a new refactoring history model merge wizard.
120          */

121         public RefactoringHistoryModelMergeWizard() {
122             super(RefactoringUIMessages.RefactoringWizard_refactoring, RefactoringUIMessages.AbstractRefactoringModelMerger_wizard_title, RefactoringUIMessages.AbstractRefactoringModelMerger_wizard_description);
123         }
124     }
125
126     /**
127      * Returns the shell of the active workbench window.
128      *
129      * @return the active shell
130      */

131     private static Shell getActiveShell() {
132         final IWorkbench workbench= PlatformUI.getWorkbench();
133         if (workbench != null) {
134             final IWorkbenchWindow window= workbench.getActiveWorkbenchWindow();
135             if (window != null)
136                 return window.getShell();
137         }
138         return null;
139     }
140
141     /**
142      * Returns the projects affected by the specified refactoring history.
143      *
144      * @param history
145      * the refactoring history
146      * @return the affected projects, or <code>null</code> if the entire
147      * workspace is affected
148      */

149     private static IProject[] getAffectedProjects(final RefactoringHistory history) {
150         final Set JavaDoc set= new HashSet JavaDoc();
151         final RefactoringDescriptorProxy[] proxies= history.getDescriptors();
152         final IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
153         for (int index= 0; index < proxies.length; index++) {
154             final String JavaDoc name= proxies[index].getProject();
155             if (name != null && !"".equals(name)) //$NON-NLS-1$
156
set.add(root.getProject(name));
157             else
158                 return null;
159         }
160         final IProject[] projects= new IProject[set.size()];
161         set.toArray(projects);
162         return projects;
163     }
164
165     /**
166      * Return a shell that can be used by the operation to display dialogs, etc.
167      *
168      * @return a shell
169      */

170     private static Shell getDialogShell() {
171         final Shell[] shell= new Shell[] { null};
172         Display.getDefault().syncExec(new Runnable JavaDoc() {
173
174             public final void run() {
175                 shell[0]= getActiveShell();
176             }
177         });
178         return shell[0];
179     }
180
181     /** The model provider */
182     private final ModelProvider fModelProvider;
183
184     /**
185      * Creates a new abstract refactoring model merger.
186      *
187      * @param provider
188      * the model provider
189      */

190     protected AbstractResourceMappingMerger(final ModelProvider provider) {
191         Assert.isNotNull(provider);
192         fModelProvider= provider;
193     }
194
195     /**
196      * Hook method which is called before the actual merge process happens.
197      * <p>
198      * Subclasses may extend this method to perform any special processing. The
199      * default implementation checks whether there are any pending refactorings
200      * in the merge context and displays a refactoring wizard to let the user
201      * perform the pending refactorings before merge.
202      * </p>
203      * <p>
204      * Returning a status of severity {@link IStatus#ERROR} will terminate the
205      * merge process.
206      * </p>
207      *
208      * @param context
209      * the merge context
210      * @param monitor
211      * the progress monitor to use
212      * @return a status describing the outcome of the operation
213      */

214     protected IStatus aboutToPerformMerge(final IMergeContext context, final IProgressMonitor monitor) {
215         Assert.isNotNull(context);
216         Assert.isNotNull(monitor);
217         try {
218             monitor.beginTask(RefactoringUIMessages.RefactoringModelMerger_merge_message, 100);
219             final IDiff[] diffs= getDiffs(context);
220             final RefactoringHistory history= getRefactoringHistory(diffs, monitor);
221             if (history != null && !history.isEmpty()) {
222                 boolean execute= true;
223                 final IProject[] projects= getAffectedProjects(history);
224                 if (projects != null) {
225                     final IProject[] dependencies= getDependencies(projects);
226                     if (dependencies.length == 0)
227                         execute= false;
228                 }
229                 if (execute) {
230                     final Shell shell= getDialogShell();
231                     shell.getDisplay().syncExec(new Runnable JavaDoc() {
232
233                         public final void run() {
234                             if (MessageDialog.openQuestion(shell, RefactoringUIMessages.RefactoringWizard_refactoring, RefactoringUIMessages.AbstractRefactoringModelMerger_accept_question)) {
235                                 final RefactoringHistoryMergeWizard wizard= new RefactoringHistoryModelMergeWizard();
236                                 int result= Window.OK;
237                                 try {
238                                     wizard.setConfiguration(new RefactoringHistoryModelMergeConfiguration((projects != null && projects.length == 1) ? projects[0] : null));
239                                     wizard.setInput(history);
240                                     result= new WizardDialog(shell, wizard).open();
241                                 } finally {
242                                     if (result != Window.CANCEL)
243                                         wizard.resolveConflicts(context);
244                                 }
245                             }
246                         }
247                     });
248                 }
249             }
250         } finally {
251             monitor.done();
252         }
253         return new Status(IStatus.OK, RefactoringCore.ID_PLUGIN, 0, "", null); //$NON-NLS-1$
254
}
255
256     /**
257      * Creates a merge status.
258      *
259      * @param context
260      * the merge context
261      * @param status
262      * the status
263      * @return the resulting merge status
264      */

265     private IStatus createMergeStatus(final IMergeContext context, final IStatus status) {
266         if (status.getCode() == IMergeStatus.CONFLICTS)
267             return new MergeStatus(status.getPlugin(), status.getMessage(), context.getScope().getMappings(fModelProvider.getDescriptor().getId()));
268         return status;
269     }
270
271     /**
272      * Returns the dependent projects of the projects associated with the
273      * incoming refactorings.
274      * <p>
275      * Subclasses must implement this method to return the dependent projects
276      * according to the semantics of the associated programming language. The
277      * result of this method is used to decide whether the resource mapping
278      * merger should execute the incoming refactorings in order to fix up
279      * references in dependent projects.
280      * </p>
281      *
282      * @param projects
283      * the projects associated with the incoming refactorings in the
284      * synchronization scope.
285      * @return the dependent projects, or an empty array
286      */

287     protected abstract IProject[] getDependencies(IProject[] projects);
288
289     /**
290      * Returns the diffs from the merge context.
291      *
292      * @param context
293      * the merge context
294      * @return the diffs, or an empty array
295      */

296     private IDiff[] getDiffs(final IMergeContext context) {
297         final ResourceMapping[] mappings= context.getScope().getMappings(fModelProvider.getDescriptor().getId());
298         final Set JavaDoc set= new HashSet JavaDoc();
299         for (int index= 0; index < mappings.length; index++) {
300             final IDiff[] diffs= context.getDiffTree().getDiffs(context.getScope().getTraversals(mappings[index]));
301             for (int offset= 0; offset < diffs.length; offset++)
302                 set.add(diffs[offset]);
303         }
304         return (IDiff[]) set.toArray(new IDiff[set.size()]);
305     }
306
307     /**
308      * {@inheritDoc}
309      */

310     protected final ModelProvider getModelProvider() {
311         return fModelProvider;
312     }
313
314     /**
315      * Returns the incoming refactoring history from the diffs.
316      *
317      * @param diffs
318      * the diffs
319      * @param monitor
320      * the progress monitor to use
321      * @return the incoming refactoring history
322      */

323     private RefactoringHistory getRefactoringHistory(final IDiff[] diffs, final IProgressMonitor monitor) {
324         final Set JavaDoc result= new HashSet JavaDoc();
325         try {
326             monitor.beginTask(RefactoringUIMessages.RefactoringModelMerger_retrieving_refactorings, diffs.length * 2);
327             for (int index= 0; index < diffs.length; index++) {
328                 final IDiff diff= diffs[index];
329                 if (diff instanceof IThreeWayDiff) {
330                     final IThreeWayDiff threeWay= (IThreeWayDiff) diff;
331                     final Set JavaDoc localDescriptors= new HashSet JavaDoc();
332                     final Set JavaDoc remoteDescriptors= new HashSet JavaDoc();
333                     final ITwoWayDiff localDiff= threeWay.getLocalChange();
334                     if (localDiff instanceof IResourceDiff && localDiff.getKind() != IDiff.NO_CHANGE) {
335                         final IResourceDiff resourceDiff= (IResourceDiff) localDiff;
336                         final IFileRevision revision= resourceDiff.getAfterState();
337                         if (revision != null) {
338                             final String JavaDoc name= revision.getName();
339                             if (name.equalsIgnoreCase(RefactoringHistoryService.NAME_HISTORY_FILE))
340                                 getRefactoringDescriptors(revision, localDescriptors, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
341                         }
342                     }
343                     final ITwoWayDiff remoteDiff= threeWay.getLocalChange();
344                     if (remoteDiff instanceof IResourceDiff && remoteDiff.getKind() != IDiff.NO_CHANGE) {
345                         final IResourceDiff resourceDiff= (IResourceDiff) remoteDiff;
346                         final IFileRevision revision= resourceDiff.getAfterState();
347                         if (revision != null) {
348                             final String JavaDoc name= revision.getName();
349                             if (name.equalsIgnoreCase(RefactoringHistoryService.NAME_HISTORY_FILE))
350                                 getRefactoringDescriptors(revision, remoteDescriptors, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
351                         }
352                     }
353                     remoteDescriptors.removeAll(localDescriptors);
354                     result.addAll(remoteDescriptors);
355                 }
356             }
357         } finally {
358             monitor.done();
359         }
360         return new RefactoringHistoryImplementation((RefactoringDescriptorProxy[]) result.toArray(new RefactoringDescriptorProxy[result.size()]));
361     }
362
363     /**
364      * Retrieves the refactoring descriptors contained in the specified file
365      * revision.
366      *
367      * @param revision
368      * the file revision
369      * @param descriptors
370      * the refactoring descriptor set to populate
371      * @param monitor
372      * the progress monitor to use
373      */

374     static final void getRefactoringDescriptors(final IFileRevision revision, final Set JavaDoc descriptors, final IProgressMonitor monitor) {
375         try {
376             monitor.beginTask(RefactoringUIMessages.RefactoringModelMerger_retrieving_refactorings, 1);
377             IStorage storage= null;
378             try {
379                 storage= revision.getStorage(new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
380             } catch (CoreException exception) {
381                 RefactoringUIPlugin.log(exception);
382             }
383             if (storage != null) {
384                 InputStream JavaDoc stream= null;
385                 try {
386                     stream= storage.getContents();
387                     final RefactoringHistory history= RefactoringHistoryService.getInstance().readRefactoringHistory(stream, RefactoringDescriptor.MULTI_CHANGE);
388                     final RefactoringDescriptorProxy[] proxies= history.getDescriptors();
389                     for (int offset= 0; offset < proxies.length; offset++)
390                         descriptors.add(proxies[offset].requestDescriptor(null));
391                 } catch (CoreException exception) {
392                     RefactoringUIPlugin.log(exception);
393                 } finally {
394                     if (stream != null) {
395                         try {
396                             stream.close();
397                         } catch (IOException JavaDoc exception) {
398                             // Do nothing
399
}
400                     }
401                 }
402             }
403         } finally {
404             monitor.done();
405         }
406     }
407
408     /**
409      * {@inheritDoc}
410      */

411     public IStatus merge(final IMergeContext context, IProgressMonitor monitor) throws CoreException {
412         Assert.isNotNull(context);
413         IStatus status= new Status(IStatus.OK, RefactoringCore.ID_PLUGIN, 0, "", null); //$NON-NLS-1$
414
if (monitor == null)
415             monitor= new NullProgressMonitor();
416         try {
417             monitor.beginTask(RefactoringUIMessages.RefactoringModelMerger_merge_message, 200);
418 // status= aboutToPerformMerge(context, new SubProgressMonitor(monitor, 75));
419
if (status.getSeverity() != IStatus.ERROR) {
420                 final IDiff[] diffs= getDiffs(context);
421                 status= createMergeStatus(context, context.merge(diffs, false, new SubProgressMonitor(monitor, 100)));
422                 final int code= status.getCode();
423                 if (status.getSeverity() != IStatus.ERROR && code != IMergeStatus.CONFLICTS && code != IMergeStatus.INTERNAL_ERROR)
424                     status= mergePerformed(context, new SubProgressMonitor(monitor, 25));
425             }
426         } finally {
427             monitor.done();
428         }
429         return status;
430     }
431
432     /**
433      * Hook method which is called after the actual merge process happened. This
434      * method is only called if {@link #merge(IMergeContext, IProgressMonitor)}
435      * returns a status with severity less than {@link IStatus#ERROR} and a
436      * status code unequal to {@link IMergeStatus#CONFLICTS} or
437      * {@link IMergeStatus#INTERNAL_ERROR}.
438      * <p>
439      * Subclasses may extend this method to perform any special processing. The
440      * default implementation does nothing.
441      * </p>
442      *
443      * @param context
444      * the merge context
445      * @param monitor
446      * the progress monitor to use
447      * @return a status describing the outcome of the operation
448      */

449     protected IStatus mergePerformed(final IMergeContext context, final IProgressMonitor monitor) {
450         Assert.isNotNull(context);
451         Assert.isNotNull(monitor);
452         try {
453             monitor.beginTask(RefactoringUIMessages.RefactoringModelMerger_merge_message, 1);
454             return new Status(IStatus.OK, RefactoringCore.ID_PLUGIN, 0, "", null); //$NON-NLS-1$
455
} finally {
456             monitor.done();
457         }
458     }
459 }
460
Popular Tags