KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > ide > undo > WorkspaceUndoUtil


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
12 package org.eclipse.ui.ide.undo;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.List JavaDoc;
16
17 import org.eclipse.core.commands.operations.IUndoContext;
18 import org.eclipse.core.commands.operations.ObjectUndoContext;
19 import org.eclipse.core.resources.IContainer;
20 import org.eclipse.core.resources.IFile;
21 import org.eclipse.core.resources.IProject;
22 import org.eclipse.core.resources.IResource;
23 import org.eclipse.core.resources.IResourceStatus;
24 import org.eclipse.core.resources.IWorkspace;
25 import org.eclipse.core.resources.IWorkspaceRoot;
26 import org.eclipse.core.resources.ResourcesPlugin;
27 import org.eclipse.core.runtime.CoreException;
28 import org.eclipse.core.runtime.IAdaptable;
29 import org.eclipse.core.runtime.IPath;
30 import org.eclipse.core.runtime.IProgressMonitor;
31 import org.eclipse.core.runtime.IStatus;
32 import org.eclipse.core.runtime.MultiStatus;
33 import org.eclipse.core.runtime.NullProgressMonitor;
34 import org.eclipse.core.runtime.OperationCanceledException;
35 import org.eclipse.core.runtime.Status;
36 import org.eclipse.core.runtime.SubProgressMonitor;
37 import org.eclipse.jface.dialogs.IDialogConstants;
38 import org.eclipse.jface.dialogs.MessageDialog;
39 import org.eclipse.osgi.util.NLS;
40 import org.eclipse.swt.widgets.Shell;
41 import org.eclipse.ui.PlatformUI;
42 import org.eclipse.ui.internal.WorkbenchPlugin;
43 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
44 import org.eclipse.ui.internal.ide.undo.ContainerDescription;
45 import org.eclipse.ui.internal.ide.undo.FileDescription;
46 import org.eclipse.ui.internal.ide.undo.UndoMessages;
47
48 /**
49  * WorkspaceUndoUtil defines common utility methods and constants used by
50  * clients who create undoable workspace operations.
51  *
52  * @since 3.3
53  *
54  */

55 public class WorkspaceUndoUtil {
56
57     private static ObjectUndoContext tasksUndoContext;
58
59     private static ObjectUndoContext bookmarksUndoContext;
60
61     /**
62      * Return the undo context that should be used for workspace-wide operations
63      *
64      * @return the undo context suitable for workspace-level operations.
65      */

66     public static IUndoContext getWorkspaceUndoContext() {
67         return WorkbenchPlugin.getDefault().getOperationSupport()
68                 .getUndoContext();
69     }
70
71     /**
72      * Return the undo context that should be used for operations involving
73      * tasks.
74      *
75      * @return the tasks undo context
76      */

77     public static IUndoContext getTasksUndoContext() {
78         if (tasksUndoContext == null) {
79             tasksUndoContext = new ObjectUndoContext(new Object JavaDoc(),
80                     "Tasks Context"); //$NON-NLS-1$
81
tasksUndoContext.addMatch(getWorkspaceUndoContext());
82         }
83         return tasksUndoContext;
84     }
85
86     /**
87      * Return the undo context that should be used for operations involving
88      * bookmarks.
89      *
90      * @return the bookmarks undo context
91      */

92     public static IUndoContext getBookmarksUndoContext() {
93         if (bookmarksUndoContext == null) {
94             bookmarksUndoContext = new ObjectUndoContext(new Object JavaDoc(),
95                     "Bookmarks Context"); //$NON-NLS-1$
96
bookmarksUndoContext.addMatch(getWorkspaceUndoContext());
97         }
98         return bookmarksUndoContext;
99     }
100
101     /**
102      * Make an <code>IAdaptable</code> that adapts to the specified shell,
103      * suitable for passing for passing to any
104      * {@link org.eclipse.core.commands.operations.IUndoableOperation} or
105      * {@link org.eclipse.core.commands.operations.IOperationHistory} method
106      * that requires an {@link org.eclipse.core.runtime.IAdaptable}
107      * <code>uiInfo</code> parameter.
108      *
109      * @param shell
110      * the shell that should be returned by the IAdaptable when asked
111      * to adapt a shell. If this parameter is <code>null</code>,
112      * the returned shell will also be <code>null</code>.
113      *
114      * @return an IAdaptable that will return the specified shell.
115      */

116     public static IAdaptable getUIInfoAdapter(final Shell shell) {
117         return new IAdaptable() {
118             public Object JavaDoc getAdapter(Class JavaDoc clazz) {
119                 if (clazz == Shell.class) {
120                     return shell;
121                 }
122                 return null;
123             }
124         };
125     }
126
127     private WorkspaceUndoUtil() {
128         // should not construct
129
}
130
131     /**
132      * Delete all of the specified resources, returning resource descriptions
133      * that can be used to restore them.
134      *
135      * @param resourcesToDelete
136      * an array of resources to be deleted
137      * @param monitor
138      * the progress monitor to use to show the operation's progress
139      * @param uiInfo
140      * the IAdaptable (or <code>null</code>) provided by the
141      * caller in order to supply UI information for prompting the
142      * user if necessary. When this parameter is not
143      * <code>null</code>, it contains an adapter for the
144      * org.eclipse.swt.widgets.Shell.class
145      *
146      * @param deleteContent
147      * a boolean indicating whether project content should be deleted
148      * when a project resource is to be deleted
149      * @return an array of ResourceDescriptions that can be used to restore the
150      * deleted resources.
151      * @throws CoreException
152      * propagates any CoreExceptions thrown from the resources API
153      */

154     static ResourceDescription[] delete(IResource[] resourcesToDelete,
155             IProgressMonitor monitor, IAdaptable uiInfo, boolean deleteContent)
156             throws CoreException {
157         final List JavaDoc exceptions = new ArrayList JavaDoc();
158         boolean forceOutOfSyncDelete = false;
159         ResourceDescription[] returnedResourceDescriptions = new ResourceDescription[resourcesToDelete.length];
160         monitor.beginTask("", resourcesToDelete.length); //$NON-NLS-1$
161
monitor
162                 .setTaskName(UndoMessages.AbstractResourcesOperation_DeleteResourcesProgress);
163         try {
164             for (int i = 0; i < resourcesToDelete.length; ++i) {
165                 if (monitor.isCanceled()) {
166                     throw new OperationCanceledException();
167                 }
168                 IResource resource = resourcesToDelete[i];
169                 try {
170                     returnedResourceDescriptions[i] = delete(resource,
171                             new SubProgressMonitor(monitor, 1), uiInfo,
172                             forceOutOfSyncDelete, deleteContent);
173                 } catch (CoreException e) {
174                     if (resource.getType() == IResource.FILE) {
175                         IStatus[] children = e.getStatus().getChildren();
176                         if (children.length == 1
177                                 && children[0].getCode() == IResourceStatus.OUT_OF_SYNC_LOCAL) {
178                             int result = queryDeleteOutOfSync(resource, uiInfo);
179
180                             if (result == IDialogConstants.YES_ID) {
181                                 // retry the delete with a force out of sync
182
delete(resource, new SubProgressMonitor(
183                                         monitor, 1), uiInfo, true,
184                                         deleteContent);
185                             } else if (result == IDialogConstants.YES_TO_ALL_ID) {
186                                 // all future attempts should force out of
187
// sync
188
forceOutOfSyncDelete = true;
189                                 delete(resource, new SubProgressMonitor(
190                                         monitor, 1), uiInfo,
191                                         forceOutOfSyncDelete, deleteContent);
192                             } else if (result == IDialogConstants.CANCEL_ID) {
193                                 throw new OperationCanceledException();
194                             } else {
195                                 exceptions.add(e);
196                             }
197                         } else {
198                             exceptions.add(e);
199                         }
200                     } else {
201                         exceptions.add(e);
202                     }
203                 }
204             }
205             IStatus result = createResult(exceptions);
206             if (!result.isOK()) {
207                 throw new CoreException(result);
208             }
209         } finally {
210             monitor.done();
211         }
212         return returnedResourceDescriptions;
213     }
214
215     /**
216      * Copies the resources to the given destination. This method can be called
217      * recursively to merge folders during folder copy.
218      *
219      * @param resources
220      * the resources to be copied
221      * @param destination
222      * the destination path for the resources, relative to the
223      * workspace
224      * @param resourcesAtDestination
225      * A list used to record the new copies.
226      * @param monitor
227      * the progress monitor used to show progress
228      * @param uiInfo
229      * the IAdaptable (or <code>null</code>) provided by the
230      * caller in order to supply UI information for prompting the
231      * user if necessary. When this parameter is not
232      * <code>null</code>, it contains an adapter for the
233      * org.eclipse.swt.widgets.Shell.class
234      * @param pathIncludesName
235      * a boolean that indicates whether the specified path includes
236      * the resource's name at the destination. If this value is
237      * <code>true</code>, the destination will contain the desired
238      * name of the resource (usually only desired when only one
239      * resource is being copied). If this value is <code>false</code>,
240      * each resource's name will be appended to the destination.
241      * @return an array of ResourceDescriptions describing any resources that
242      * were overwritten by the copy operation
243      * @throws CoreException
244      * propagates any CoreExceptions thrown from the resources API
245      */

246     static ResourceDescription[] copy(IResource[] resources, IPath destination,
247             List JavaDoc resourcesAtDestination, IProgressMonitor monitor,
248             IAdaptable uiInfo, boolean pathIncludesName) throws CoreException {
249
250         monitor.beginTask("", resources.length); //$NON-NLS-1$
251
monitor
252                 .setTaskName(UndoMessages.AbstractResourcesOperation_CopyingResourcesProgress);
253         List JavaDoc overwrittenResources = new ArrayList JavaDoc();
254         for (int i = 0; i < resources.length; i++) {
255             IResource source = resources[i];
256             IPath destinationPath;
257             if (pathIncludesName) {
258                 destinationPath = destination;
259             } else {
260                 destinationPath = destination.append(source.getName());
261             }
262             IWorkspaceRoot workspaceRoot = getWorkspaceRoot();
263             IResource existing = workspaceRoot.findMember(destinationPath);
264             if (source.getType() == IResource.FOLDER && existing != null) {
265                 // The resource is a folder and it exists in the destination.
266
// Copy its children to the existing destination.
267
if (source.isLinked() == existing.isLinked()) {
268                     IResource[] children = ((IContainer) source).members();
269                     ResourceDescription[] overwritten = copy(children,
270                             destinationPath, resourcesAtDestination,
271                             new SubProgressMonitor(monitor, 1), uiInfo, false);
272                     // We don't record the copy since this recursive call will
273
// do so. Just record the overwrites.
274
for (int j = 0; j < overwritten.length; j++) {
275                         overwrittenResources.add(overwritten[j]);
276                     }
277                 } else {
278                     // delete the destination folder, copying a linked folder
279
// over an unlinked one or vice versa. Fixes bug 28772.
280
ResourceDescription[] deleted = delete(
281                             new IResource[] { existing },
282                             new SubProgressMonitor(monitor, 0), uiInfo, false);
283                     source.copy(destinationPath, IResource.SHALLOW,
284                             new SubProgressMonitor(monitor, 1));
285                     // Record the copy
286
resourcesAtDestination.add(getWorkspace().getRoot()
287                             .findMember(destinationPath));
288                     for (int j = 0; j < deleted.length; j++) {
289                         overwrittenResources.add(deleted[j]);
290                     }
291                 }
292             } else {
293                 if (existing != null) {
294                     if (source.isLinked() == existing.isLinked()) {
295                         overwrittenResources.add(copyOverExistingResource(
296                                 source, existing, new SubProgressMonitor(
297                                         monitor, 1), uiInfo, false));
298                         // Record the "copy"
299
resourcesAtDestination.add(existing);
300                     } else {
301                         // Copying a linked resource over unlinked or vice
302
// versa. Can't use setContents here. Fixes bug 28772.
303
ResourceDescription[] deleted = delete(
304                                 new IResource[] { existing },
305                                 new SubProgressMonitor(monitor, 0), uiInfo,
306                                 false);
307                         source.copy(destinationPath, IResource.SHALLOW,
308                                 new SubProgressMonitor(monitor, 1));
309                         // Record the copy
310
resourcesAtDestination.add(getWorkspace().getRoot()
311                                 .findMember(destinationPath));
312                         for (int j = 0; j < deleted.length; j++) {
313                             overwrittenResources.add(deleted[j]);
314                         }
315                     }
316                 } else {
317                     // no resources are being overwritten
318
// ensure the destination path exists
319
IPath parentPath = destination;
320                     if (pathIncludesName) {
321                         parentPath = destination.removeLastSegments(1);
322                     }
323                     IContainer generatedParent = generateContainers(parentPath);
324                     source.copy(destinationPath, IResource.SHALLOW,
325                             new SubProgressMonitor(monitor, 1));
326                     // Record the copy. If we had to generate a parent
327
// folder, that should be recorded as part of the copy
328
if (generatedParent == null) {
329                         resourcesAtDestination.add(getWorkspace().getRoot()
330                                 .findMember(destinationPath));
331                     } else {
332                         resourcesAtDestination.add(generatedParent);
333                     }
334                 }
335
336                 if (monitor.isCanceled()) {
337                     throw new OperationCanceledException();
338                 }
339             }
340         }
341         monitor.done();
342         return (ResourceDescription[]) overwrittenResources
343                 .toArray(new ResourceDescription[overwrittenResources.size()]);
344
345     }
346
347     /**
348      * Moves the resources to the given destination. This method can be called
349      * recursively to merge folders during folder move.
350      *
351      * @param resources
352      * the resources to be moved
353      * @param destination
354      * the destination path for the resources, relative to the
355      * workspace
356      * @param resourcesAtDestination
357      * A list used to record each moved resource.
358      * @param reverseDestinations
359      * A list used to record each moved resource's original location
360      * @param monitor
361      * the progress monitor used to show progress
362      * @param uiInfo
363      * the IAdaptable (or <code>null</code>) provided by the
364      * caller in order to supply UI information for prompting the
365      * user if necessary. When this parameter is not
366      * <code>null</code>, it contains an adapter for the
367      * org.eclipse.swt.widgets.Shell.class
368      * @param pathIncludesName
369      * a boolean that indicates whether the specified path includes
370      * the resource's name at the destination. If this value is
371      * <code>true</code>, the destination will contain the desired
372      * name of the resource (usually only desired when only one
373      * resource is being moved). If this value is <code>false</code>,
374      * each resource's name will be appended to the destination.
375      * @return an array of ResourceDescriptions describing any resources that
376      * were overwritten by the move operation
377      * @throws CoreException
378      * propagates any CoreExceptions thrown from the resources API
379      */

380     static ResourceDescription[] move(IResource[] resources, IPath destination,
381             List JavaDoc resourcesAtDestination, List JavaDoc reverseDestinations,
382             IProgressMonitor monitor, IAdaptable uiInfo,
383             boolean pathIncludesName) throws CoreException {
384
385         monitor.beginTask("", resources.length); //$NON-NLS-1$
386
monitor
387                 .setTaskName(UndoMessages.AbstractResourcesOperation_MovingResources);
388         List JavaDoc overwrittenResources = new ArrayList JavaDoc();
389         for (int i = 0; i < resources.length; i++) {
390             IResource source = resources[i];
391             IPath destinationPath;
392             if (pathIncludesName) {
393                 destinationPath = destination;
394             } else {
395                 destinationPath = destination.append(source.getName());
396             }
397             IWorkspaceRoot workspaceRoot = getWorkspaceRoot();
398             IResource existing = workspaceRoot.findMember(destinationPath);
399             if (source.getType() == IResource.FOLDER && existing != null) {
400                 // The resource is a folder and it exists in the destination.
401
// Move its children to the existing destination.
402
if (source.isLinked() == existing.isLinked()) {
403                     IResource[] children = ((IContainer) source).members();
404                     ResourceDescription[] overwritten = move(children,
405                             destinationPath, resourcesAtDestination,
406                             reverseDestinations, new SubProgressMonitor(
407                                     monitor, 1), uiInfo, false);
408                     // We don't record the moved resources since the recursive
409
// call has done so. Just record the overwrites.
410
for (int j = 0; j < overwritten.length; j++) {
411                         overwrittenResources.add(overwritten[j]);
412                     }
413                     // Delete the source. No need to record it since it
414
// will get moved back.
415
delete(source, monitor, uiInfo, false, false);
416                 } else {
417                     // delete the destination folder, moving a linked folder
418
// over an unlinked one or vice versa. Fixes bug 28772.
419
ResourceDescription[] deleted = delete(
420                             new IResource[] { existing },
421                             new SubProgressMonitor(monitor, 0), uiInfo, false);
422                     // Record the original path
423
reverseDestinations.add(source.getFullPath());
424                     source.move(destinationPath, IResource.SHALLOW
425                             | IResource.KEEP_HISTORY, new SubProgressMonitor(
426                             monitor, 1));
427                     // Record the resource at its destination
428
resourcesAtDestination.add(getWorkspace().getRoot()
429                             .findMember(destinationPath));
430                     for (int j = 0; j < deleted.length; j++) {
431                         overwrittenResources.add(deleted[j]);
432                     }
433                 }
434             } else {
435                 if (existing != null) {
436                     if (source.isLinked() == existing.isLinked()) {
437                         // Record the original path
438
reverseDestinations.add(source.getFullPath());
439                         overwrittenResources.add(copyOverExistingResource(
440                                 source, existing, new SubProgressMonitor(
441                                         monitor, 1), uiInfo, true));
442                         resourcesAtDestination.add(existing);
443                     } else {
444                         // Moving a linked resource over unlinked or vice
445
// versa. Can't use setContents here. Fixes bug 28772.
446
ResourceDescription[] deleted = delete(
447                                 new IResource[] { existing },
448                                 new SubProgressMonitor(monitor, 0), uiInfo,
449                                 false);
450                         reverseDestinations.add(source.getFullPath());
451                         source.move(destinationPath, IResource.SHALLOW
452                                 | IResource.KEEP_HISTORY,
453                                 new SubProgressMonitor(monitor, 1));
454                         // Record the resource at its destination
455
resourcesAtDestination.add(getWorkspace().getRoot()
456                                 .findMember(destinationPath));
457                         for (int j = 0; j < deleted.length; j++) {
458                             overwrittenResources.add(deleted[j]);
459                         }
460                     }
461                 } else {
462                     // No resources are being overwritten.
463
// First record the source path
464
reverseDestinations.add(source.getFullPath());
465                     // ensure the destination path exists
466
IPath parentPath = destination;
467                     if (pathIncludesName) {
468                         parentPath = destination.removeLastSegments(1);
469                     }
470
471                     IContainer generatedParent = generateContainers(parentPath);
472                     source.move(destinationPath, IResource.SHALLOW
473                             | IResource.KEEP_HISTORY, new SubProgressMonitor(
474                             monitor, 1));
475                     // Record the move. If we had to generate a parent
476
// folder, that should be recorded as part of the copy
477
if (generatedParent == null) {
478                         resourcesAtDestination.add(getWorkspace().getRoot()
479                                 .findMember(destinationPath));
480                     } else {
481                         resourcesAtDestination.add(generatedParent);
482                     }
483                 }
484
485                 if (monitor.isCanceled()) {
486                     throw new OperationCanceledException();
487                 }
488             }
489         }
490         monitor.done();
491         return (ResourceDescription[]) overwrittenResources
492                 .toArray(new ResourceDescription[overwrittenResources.size()]);
493
494     }
495
496     /**
497      * Recreate the resources from the specified resource descriptions.
498      *
499      * @param resourcesToRecreate
500      * the ResourceDescriptions describing resources to be recreated
501      * @param monitor
502      * the progress monitor used to show progress
503      * @param uiInfo
504      * the IAdaptable (or <code>null</code>) provided by the
505      * caller in order to supply UI information for prompting the
506      * user if necessary. When this parameter is not
507      * <code>null</code>, it contains an adapter for the
508      * org.eclipse.swt.widgets.Shell.class
509      * @return an array of resources that were created
510      * @throws CoreException
511      * propagates any CoreExceptions thrown from the resources API
512      */

513     static IResource[] recreate(ResourceDescription[] resourcesToRecreate,
514             IProgressMonitor monitor, IAdaptable uiInfo) throws CoreException {
515         final List JavaDoc exceptions = new ArrayList JavaDoc();
516         IResource[] resourcesToReturn = new IResource[resourcesToRecreate.length];
517         monitor.beginTask("", resourcesToRecreate.length); //$NON-NLS-1$
518
monitor
519                 .setTaskName(UndoMessages.AbstractResourcesOperation_CreateResourcesProgress);
520         try {
521             for (int i = 0; i < resourcesToRecreate.length; ++i) {
522                 if (monitor.isCanceled()) {
523                     throw new OperationCanceledException();
524                 }
525                 try {
526                     resourcesToReturn[i] = resourcesToRecreate[i]
527                             .createResource(new SubProgressMonitor(monitor, 1));
528                 } catch (CoreException e) {
529                     exceptions.add(e);
530                 }
531             }
532             IStatus result = WorkspaceUndoUtil.createResult(exceptions);
533             if (!result.isOK()) {
534                 throw new CoreException(result);
535             }
536         } finally {
537             monitor.done();
538         }
539         return resourcesToReturn;
540     }
541
542     /**
543      * Delete the specified resources, returning a resource description that can
544      * be used to restore it.
545      *
546      * @param resourceToDelete
547      * the resource to be deleted
548      * @param monitor
549      * the progress monitor to use to show the operation's progress
550      * @param uiInfo
551      * the IAdaptable (or <code>null</code>) provided by the
552      * caller in order to supply UI information for prompting the
553      * user if necessary. When this parameter is not
554      * <code>null</code>, it contains an adapter for the
555      * org.eclipse.swt.widgets.Shell.class
556      * @param forceOutOfSyncDelete
557      * a boolean indicating whether a resource should be deleted even
558      * if it is out of sync with the file system
559      * @param deleteContent
560      * a boolean indicating whether project content should be deleted
561      * when a project resource is to be deleted
562      * @return a ResourceDescription that can be used to restore the deleted
563      * resource.
564      * @throws CoreException
565      * propagates any CoreExceptions thrown from the resources API
566      */

567     static ResourceDescription delete(IResource resourceToDelete,
568             IProgressMonitor monitor, IAdaptable uiInfo,
569             boolean forceOutOfSyncDelete, boolean deleteContent)
570             throws CoreException {
571         ResourceDescription resourceDescription = ResourceDescription
572                 .fromResource(resourceToDelete);
573         if (resourceToDelete.getType() == IResource.PROJECT) {
574             // it is a project
575
monitor
576                     .setTaskName(UndoMessages.AbstractResourcesOperation_DeleteResourcesProgress);
577             IProject project = (IProject) resourceToDelete;
578             project.delete(deleteContent, forceOutOfSyncDelete, monitor);
579         } else {
580             // if it's not a project, just delete it
581
monitor.beginTask("", 2); //$NON-NLS-1$
582
monitor
583                     .setTaskName(UndoMessages.AbstractResourcesOperation_DeleteResourcesProgress);
584             int updateFlags;
585             if (forceOutOfSyncDelete) {
586                 updateFlags = IResource.KEEP_HISTORY | IResource.FORCE;
587             } else {
588                 updateFlags = IResource.KEEP_HISTORY;
589             }
590             resourceToDelete.delete(updateFlags, new SubProgressMonitor(
591                     monitor, 1));
592             resourceDescription.recordStateFromHistory(resourceToDelete,
593                     new SubProgressMonitor(monitor, 1));
594             monitor.done();
595         }
596
597         return resourceDescription;
598     }
599
600     /*
601      * Copy the content of the specified resource to the existing resource,
602      * returning a ResourceDescription that can be used to restore the original
603      * content. Do nothing if the resources are not files.
604      */

605     private static ResourceDescription copyOverExistingResource(
606             IResource source, IResource existing, IProgressMonitor monitor,
607             IAdaptable uiInfo, boolean deleteSourceFile) throws CoreException {
608         if (!(source instanceof IFile && existing instanceof IFile)) {
609             return null;
610         }
611         IFile file = (IFile) source;
612         IFile existingFile = (IFile) existing;
613         monitor
614                 .beginTask(
615                         UndoMessages.AbstractResourcesOperation_CopyingResourcesProgress,
616                         3);
617         if (file != null && existingFile != null) {
618             if (validateEdit(file, existingFile, getShell(uiInfo))) {
619                 // Remember the state of the existing file so it can be
620
// restored.
621
FileDescription fileDescription = new FileDescription(
622                         existingFile);
623                 // Reset the contents to that of the file being moved
624
existingFile.setContents(file.getContents(),
625                         IResource.KEEP_HISTORY, new SubProgressMonitor(monitor,
626                                 1));
627                 fileDescription.recordStateFromHistory(existingFile,
628                         new SubProgressMonitor(monitor, 1));
629                 // Now delete the source file if requested
630
// We don't need to remember anything about it, because
631
// any undo involving this operation will move the original
632
// content back to it.
633
if (deleteSourceFile) {
634                     file.delete(IResource.KEEP_HISTORY, new SubProgressMonitor(
635                             monitor, 1));
636                 }
637                 monitor.done();
638                 return fileDescription;
639             }
640         }
641         monitor.done();
642         return null;
643     }
644
645     /*
646      * Check for existence of the specified path and generate any containers
647      * that do not yet exist. Return any generated containers, or null if no
648      * container had to be generated.
649      */

650     private static IContainer generateContainers(IPath path)
651             throws CoreException {
652         IResource container;
653         if (path.segmentCount() == 0) {
654             // nothing to generate
655
return null;
656         }
657         container = getWorkspaceRoot().findMember(path);
658         // Nothing to generate because container exists
659
if (container != null) {
660             return null;
661         }
662
663         // Now make a non-existent handle representing the desired container
664
if (path.segmentCount() == 1) {
665             container = ResourcesPlugin.getWorkspace().getRoot().getProject(
666                     path.segment(0));
667         } else {
668             container = ResourcesPlugin.getWorkspace().getRoot()
669                     .getFolder(path);
670         }
671         ContainerDescription containerDescription = ContainerDescription
672                 .fromContainer((IContainer) container);
673         container = containerDescription.createResourceHandle();
674         containerDescription.createExistentResourceFromHandle(container,
675                 new NullProgressMonitor());
676         return (IContainer) container;
677     }
678
679     /*
680      * Ask the user whether the given resource should be deleted despite being
681      * out of sync with the file system.
682      *
683      * Return one of the IDialogConstants constants indicating which of the Yes,
684      * Yes to All, No, Cancel options has been selected by the user.
685      */

686     private static int queryDeleteOutOfSync(IResource resource,
687             IAdaptable uiInfo) {
688         Shell shell = getShell(uiInfo);
689         final MessageDialog dialog = new MessageDialog(
690                 shell,
691                 UndoMessages.AbstractResourcesOperation_deletionMessageTitle,
692                 null,
693                 NLS
694                         .bind(
695                                 UndoMessages.AbstractResourcesOperation_outOfSyncQuestion,
696                                 resource.getName()), MessageDialog.QUESTION,
697                 new String JavaDoc[] { IDialogConstants.YES_LABEL,
698                         IDialogConstants.YES_TO_ALL_LABEL,
699                         IDialogConstants.NO_LABEL,
700                         IDialogConstants.CANCEL_LABEL }, 0);
701         shell.getDisplay().syncExec(new Runnable JavaDoc() {
702             public void run() {
703                 dialog.open();
704             }
705         });
706         int result = dialog.getReturnCode();
707         if (result == 0) {
708             return IDialogConstants.YES_ID;
709         }
710         if (result == 1) {
711             return IDialogConstants.YES_TO_ALL_ID;
712         }
713         if (result == 2) {
714             return IDialogConstants.NO_ID;
715         }
716         return IDialogConstants.CANCEL_ID;
717     }
718
719     /*
720      * Creates and return a result status appropriate for the given list of
721      * exceptions.
722      */

723     private static IStatus createResult(List JavaDoc exceptions) {
724         if (exceptions.isEmpty()) {
725             return Status.OK_STATUS;
726         }
727         final int exceptionCount = exceptions.size();
728         if (exceptionCount == 1) {
729             return ((CoreException) exceptions.get(0)).getStatus();
730         }
731         CoreException[] children = (CoreException[]) exceptions
732                 .toArray(new CoreException[exceptionCount]);
733         boolean outOfSync = false;
734         for (int i = 0; i < children.length; i++) {
735             if (children[i].getStatus().getCode() == IResourceStatus.OUT_OF_SYNC_LOCAL) {
736                 outOfSync = true;
737                 break;
738             }
739         }
740         String JavaDoc title = outOfSync ? UndoMessages.AbstractResourcesOperation_outOfSyncError
741                 : UndoMessages.AbstractResourcesOperation_deletionExceptionMessage;
742         final MultiStatus multi = new MultiStatus(
743                 IDEWorkbenchPlugin.IDE_WORKBENCH, 0, title, null);
744         for (int i = 0; i < exceptionCount; i++) {
745             CoreException exception = children[i];
746             IStatus status = exception.getStatus();
747             multi.add(new Status(status.getSeverity(), status.getPlugin(),
748                     status.getCode(), status.getMessage(), exception));
749         }
750         return multi;
751     }
752
753     /*
754      * Return the workspace.
755      */

756     private static IWorkspace getWorkspace() {
757         return ResourcesPlugin.getWorkspace();
758     }
759
760     /*
761      * Return the workspace root.
762      */

763     private static IWorkspaceRoot getWorkspaceRoot() {
764         return getWorkspace().getRoot();
765     }
766
767     /*
768      * Validate the destination file if it is read-only and additionally the
769      * source file if both are read-only. Returns true if both files could be
770      * made writeable.
771      */

772     private static boolean validateEdit(IFile source, IFile destination,
773             Shell shell) {
774         if (destination.isReadOnly()) {
775             IWorkspace workspace = WorkspaceUndoUtil.getWorkspace();
776             IStatus status;
777             if (source.isReadOnly()) {
778                 status = workspace.validateEdit(new IFile[] { source,
779                         destination }, shell);
780             } else {
781                 status = workspace.validateEdit(new IFile[] { destination },
782                         shell);
783             }
784             return status.isOK();
785         }
786         return true;
787     }
788
789     /**
790      * Return the shell described by the specified adaptable, or the active
791      * shell if no shell has been specified in the adaptable.
792      *
793      * @param uiInfo
794      * the IAdaptable (or <code>null</code>) provided by the
795      * caller in order to supply UI information for prompting the
796      * user if necessary. When this parameter is not
797      * <code>null</code>, it contains an adapter for the
798      * org.eclipse.swt.widgets.Shell.class
799      *
800      * @return the Shell that can be used to show information
801      */

802     public static Shell getShell(IAdaptable uiInfo) {
803         if (uiInfo != null) {
804             Shell shell = (Shell) uiInfo.getAdapter(Shell.class);
805             if (shell != null) {
806                 return shell;
807             }
808         }
809         return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
810     }
811 }
812
Popular Tags