| /******************************************************************************* |
| * Copyright (c) 2006, 2007 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.ui.ide.undo; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.core.commands.operations.IUndoContext; |
| import org.eclipse.core.commands.operations.ObjectUndoContext; |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceStatus; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.internal.WorkbenchPlugin; |
| import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; |
| import org.eclipse.ui.internal.ide.undo.ContainerDescription; |
| import org.eclipse.ui.internal.ide.undo.FileDescription; |
| import org.eclipse.ui.internal.ide.undo.UndoMessages; |
| |
| /** |
| * WorkspaceUndoUtil defines common utility methods and constants used by |
| * clients who create undoable workspace operations. |
| * |
| * @since 3.3 |
| * |
| */ |
| public class WorkspaceUndoUtil { |
| |
| private static ObjectUndoContext tasksUndoContext; |
| |
| private static ObjectUndoContext bookmarksUndoContext; |
| |
| /** |
| * Return the undo context that should be used for workspace-wide operations |
| * |
| * @return the undo context suitable for workspace-level operations. |
| */ |
| public static IUndoContext getWorkspaceUndoContext() { |
| return WorkbenchPlugin.getDefault().getOperationSupport() |
| .getUndoContext(); |
| } |
| |
| /** |
| * Return the undo context that should be used for operations involving |
| * tasks. |
| * |
| * @return the tasks undo context |
| */ |
| public static IUndoContext getTasksUndoContext() { |
| if (tasksUndoContext == null) { |
| tasksUndoContext = new ObjectUndoContext(new Object(), |
| "Tasks Context"); //$NON-NLS-1$ |
| tasksUndoContext.addMatch(getWorkspaceUndoContext()); |
| } |
| return tasksUndoContext; |
| } |
| |
| /** |
| * Return the undo context that should be used for operations involving |
| * bookmarks. |
| * |
| * @return the bookmarks undo context |
| */ |
| public static IUndoContext getBookmarksUndoContext() { |
| if (bookmarksUndoContext == null) { |
| bookmarksUndoContext = new ObjectUndoContext(new Object(), |
| "Bookmarks Context"); //$NON-NLS-1$ |
| bookmarksUndoContext.addMatch(getWorkspaceUndoContext()); |
| } |
| return bookmarksUndoContext; |
| } |
| |
| /** |
| * Make an <code>IAdaptable</code> that adapts to the specified shell, |
| * suitable for passing for passing to any |
| * {@link org.eclipse.core.commands.operations.IUndoableOperation} or |
| * {@link org.eclipse.core.commands.operations.IOperationHistory} method |
| * that requires an {@link org.eclipse.core.runtime.IAdaptable} |
| * <code>uiInfo</code> parameter. |
| * |
| * @param shell |
| * the shell that should be returned by the IAdaptable when asked |
| * to adapt a shell. If this parameter is <code>null</code>, |
| * the returned shell will also be <code>null</code>. |
| * |
| * @return an IAdaptable that will return the specified shell. |
| */ |
| public static IAdaptable getUIInfoAdapter(final Shell shell) { |
| return new IAdaptable() { |
| public Object getAdapter(Class clazz) { |
| if (clazz == Shell.class) { |
| return shell; |
| } |
| return null; |
| } |
| }; |
| } |
| |
| private WorkspaceUndoUtil() { |
| // should not construct |
| } |
| |
| /** |
| * Delete all of the specified resources, returning resource descriptions |
| * that can be used to restore them. |
| * |
| * @param resourcesToDelete |
| * an array of resources to be deleted |
| * @param monitor |
| * the progress monitor to use to show the operation's progress |
| * @param uiInfo |
| * the IAdaptable (or <code>null</code>) provided by the |
| * caller in order to supply UI information for prompting the |
| * user if necessary. When this parameter is not |
| * <code>null</code>, it contains an adapter for the |
| * org.eclipse.swt.widgets.Shell.class |
| * |
| * @param deleteContent |
| * a boolean indicating whether project content should be deleted |
| * when a project resource is to be deleted |
| * @return an array of ResourceDescriptions that can be used to restore the |
| * deleted resources. |
| * @throws CoreException |
| * propagates any CoreExceptions thrown from the resources API |
| */ |
| static ResourceDescription[] delete(IResource[] resourcesToDelete, |
| IProgressMonitor monitor, IAdaptable uiInfo, boolean deleteContent) |
| throws CoreException { |
| final List exceptions = new ArrayList(); |
| boolean forceOutOfSyncDelete = false; |
| ResourceDescription[] returnedResourceDescriptions = new ResourceDescription[resourcesToDelete.length]; |
| monitor.beginTask("", resourcesToDelete.length); //$NON-NLS-1$ |
| monitor |
| .setTaskName(UndoMessages.AbstractResourcesOperation_DeleteResourcesProgress); |
| try { |
| for (int i = 0; i < resourcesToDelete.length; ++i) { |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| IResource resource = resourcesToDelete[i]; |
| try { |
| returnedResourceDescriptions[i] = delete(resource, |
| new SubProgressMonitor(monitor, 1), uiInfo, |
| forceOutOfSyncDelete, deleteContent); |
| } catch (CoreException e) { |
| if (resource.getType() == IResource.FILE) { |
| IStatus[] children = e.getStatus().getChildren(); |
| if (children.length == 1 |
| && children[0].getCode() == IResourceStatus.OUT_OF_SYNC_LOCAL) { |
| int result = queryDeleteOutOfSync(resource, uiInfo); |
| |
| if (result == IDialogConstants.YES_ID) { |
| // retry the delete with a force out of sync |
| delete(resource, new SubProgressMonitor( |
| monitor, 1), uiInfo, true, |
| deleteContent); |
| } else if (result == IDialogConstants.YES_TO_ALL_ID) { |
| // all future attempts should force out of |
| // sync |
| forceOutOfSyncDelete = true; |
| delete(resource, new SubProgressMonitor( |
| monitor, 1), uiInfo, |
| forceOutOfSyncDelete, deleteContent); |
| } else if (result == IDialogConstants.CANCEL_ID) { |
| throw new OperationCanceledException(); |
| } else { |
| exceptions.add(e); |
| } |
| } else { |
| exceptions.add(e); |
| } |
| } else { |
| exceptions.add(e); |
| } |
| } |
| } |
| IStatus result = createResult(exceptions); |
| if (!result.isOK()) { |
| throw new CoreException(result); |
| } |
| } finally { |
| monitor.done(); |
| } |
| return returnedResourceDescriptions; |
| } |
| |
| /** |
| * Copies the resources to the given destination. This method can be called |
| * recursively to merge folders during folder copy. |
| * |
| * @param resources |
| * the resources to be copied |
| * @param destination |
| * the destination path for the resources, relative to the |
| * workspace |
| * @param resourcesAtDestination |
| * A list used to record the new copies. |
| * @param monitor |
| * the progress monitor used to show progress |
| * @param uiInfo |
| * the IAdaptable (or <code>null</code>) provided by the |
| * caller in order to supply UI information for prompting the |
| * user if necessary. When this parameter is not |
| * <code>null</code>, it contains an adapter for the |
| * org.eclipse.swt.widgets.Shell.class |
| * @param pathIncludesName |
| * a boolean that indicates whether the specified path includes |
| * the resource's name at the destination. If this value is |
| * <code>true</code>, the destination will contain the desired |
| * name of the resource (usually only desired when only one |
| * resource is being copied). If this value is <code>false</code>, |
| * each resource's name will be appended to the destination. |
| * @return an array of ResourceDescriptions describing any resources that |
| * were overwritten by the copy operation |
| * @throws CoreException |
| * propagates any CoreExceptions thrown from the resources API |
| */ |
| static ResourceDescription[] copy(IResource[] resources, IPath destination, |
| List resourcesAtDestination, IProgressMonitor monitor, |
| IAdaptable uiInfo, boolean pathIncludesName) throws CoreException { |
| |
| monitor.beginTask("", resources.length); //$NON-NLS-1$ |
| monitor |
| .setTaskName(UndoMessages.AbstractResourcesOperation_CopyingResourcesProgress); |
| List overwrittenResources = new ArrayList(); |
| for (int i = 0; i < resources.length; i++) { |
| IResource source = resources[i]; |
| IPath destinationPath; |
| if (pathIncludesName) { |
| destinationPath = destination; |
| } else { |
| destinationPath = destination.append(source.getName()); |
| } |
| IWorkspaceRoot workspaceRoot = getWorkspaceRoot(); |
| IResource existing = workspaceRoot.findMember(destinationPath); |
| if (source.getType() == IResource.FOLDER && existing != null) { |
| // The resource is a folder and it exists in the destination. |
| // Copy its children to the existing destination. |
| if (source.isLinked() == existing.isLinked()) { |
| IResource[] children = ((IContainer) source).members(); |
| ResourceDescription[] overwritten = copy(children, |
| destinationPath, resourcesAtDestination, |
| new SubProgressMonitor(monitor, 1), uiInfo, false); |
| // We don't record the copy since this recursive call will |
| // do so. Just record the overwrites. |
| for (int j = 0; j < overwritten.length; j++) { |
| overwrittenResources.add(overwritten[j]); |
| } |
| } else { |
| // delete the destination folder, copying a linked folder |
| // over an unlinked one or vice versa. Fixes bug 28772. |
| ResourceDescription[] deleted = delete( |
| new IResource[] { existing }, |
| new SubProgressMonitor(monitor, 0), uiInfo, false); |
| source.copy(destinationPath, IResource.SHALLOW, |
| new SubProgressMonitor(monitor, 1)); |
| // Record the copy |
| resourcesAtDestination.add(getWorkspace().getRoot() |
| .findMember(destinationPath)); |
| for (int j = 0; j < deleted.length; j++) { |
| overwrittenResources.add(deleted[j]); |
| } |
| } |
| } else { |
| if (existing != null) { |
| if (source.isLinked() == existing.isLinked()) { |
| overwrittenResources.add(copyOverExistingResource( |
| source, existing, new SubProgressMonitor( |
| monitor, 1), uiInfo, false)); |
| // Record the "copy" |
| resourcesAtDestination.add(existing); |
| } else { |
| // Copying a linked resource over unlinked or vice |
| // versa. Can't use setContents here. Fixes bug 28772. |
| ResourceDescription[] deleted = delete( |
| new IResource[] { existing }, |
| new SubProgressMonitor(monitor, 0), uiInfo, |
| false); |
| source.copy(destinationPath, IResource.SHALLOW, |
| new SubProgressMonitor(monitor, 1)); |
| // Record the copy |
| resourcesAtDestination.add(getWorkspace().getRoot() |
| .findMember(destinationPath)); |
| for (int j = 0; j < deleted.length; j++) { |
| overwrittenResources.add(deleted[j]); |
| } |
| } |
| } else { |
| // no resources are being overwritten |
| // ensure the destination path exists |
| IPath parentPath = destination; |
| if (pathIncludesName) { |
| parentPath = destination.removeLastSegments(1); |
| } |
| IContainer generatedParent = generateContainers(parentPath); |
| source.copy(destinationPath, IResource.SHALLOW, |
| new SubProgressMonitor(monitor, 1)); |
| // Record the copy. If we had to generate a parent |
| // folder, that should be recorded as part of the copy |
| if (generatedParent == null) { |
| resourcesAtDestination.add(getWorkspace().getRoot() |
| .findMember(destinationPath)); |
| } else { |
| resourcesAtDestination.add(generatedParent); |
| } |
| } |
| |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| } |
| } |
| monitor.done(); |
| return (ResourceDescription[]) overwrittenResources |
| .toArray(new ResourceDescription[overwrittenResources.size()]); |
| |
| } |
| |
| /** |
| * Moves the resources to the given destination. This method can be called |
| * recursively to merge folders during folder move. |
| * |
| * @param resources |
| * the resources to be moved |
| * @param destination |
| * the destination path for the resources, relative to the |
| * workspace |
| * @param resourcesAtDestination |
| * A list used to record each moved resource. |
| * @param reverseDestinations |
| * A list used to record each moved resource's original location |
| * @param monitor |
| * the progress monitor used to show progress |
| * @param uiInfo |
| * the IAdaptable (or <code>null</code>) provided by the |
| * caller in order to supply UI information for prompting the |
| * user if necessary. When this parameter is not |
| * <code>null</code>, it contains an adapter for the |
| * org.eclipse.swt.widgets.Shell.class |
| * @param pathIncludesName |
| * a boolean that indicates whether the specified path includes |
| * the resource's name at the destination. If this value is |
| * <code>true</code>, the destination will contain the desired |
| * name of the resource (usually only desired when only one |
| * resource is being moved). If this value is <code>false</code>, |
| * each resource's name will be appended to the destination. |
| * @return an array of ResourceDescriptions describing any resources that |
| * were overwritten by the move operation |
| * @throws CoreException |
| * propagates any CoreExceptions thrown from the resources API |
| */ |
| static ResourceDescription[] move(IResource[] resources, IPath destination, |
| List resourcesAtDestination, List reverseDestinations, |
| IProgressMonitor monitor, IAdaptable uiInfo, |
| boolean pathIncludesName) throws CoreException { |
| |
| monitor.beginTask("", resources.length); //$NON-NLS-1$ |
| monitor |
| .setTaskName(UndoMessages.AbstractResourcesOperation_MovingResources); |
| List overwrittenResources = new ArrayList(); |
| for (int i = 0; i < resources.length; i++) { |
| IResource source = resources[i]; |
| IPath destinationPath; |
| if (pathIncludesName) { |
| destinationPath = destination; |
| } else { |
| destinationPath = destination.append(source.getName()); |
| } |
| IWorkspaceRoot workspaceRoot = getWorkspaceRoot(); |
| IResource existing = workspaceRoot.findMember(destinationPath); |
| if (source.getType() == IResource.FOLDER && existing != null) { |
| // The resource is a folder and it exists in the destination. |
| // Move its children to the existing destination. |
| if (source.isLinked() == existing.isLinked()) { |
| IResource[] children = ((IContainer) source).members(); |
| ResourceDescription[] overwritten = move(children, |
| destinationPath, resourcesAtDestination, |
| reverseDestinations, new SubProgressMonitor( |
| monitor, 1), uiInfo, false); |
| // We don't record the moved resources since the recursive |
| // call has done so. Just record the overwrites. |
| for (int j = 0; j < overwritten.length; j++) { |
| overwrittenResources.add(overwritten[j]); |
| } |
| // Delete the source. No need to record it since it |
| // will get moved back. |
| delete(source, monitor, uiInfo, false, false); |
| } else { |
| // delete the destination folder, moving a linked folder |
| // over an unlinked one or vice versa. Fixes bug 28772. |
| ResourceDescription[] deleted = delete( |
| new IResource[] { existing }, |
| new SubProgressMonitor(monitor, 0), uiInfo, false); |
| // Record the original path |
| reverseDestinations.add(source.getFullPath()); |
| source.move(destinationPath, IResource.SHALLOW |
| | IResource.KEEP_HISTORY, new SubProgressMonitor( |
| monitor, 1)); |
| // Record the resource at its destination |
| resourcesAtDestination.add(getWorkspace().getRoot() |
| .findMember(destinationPath)); |
| for (int j = 0; j < deleted.length; j++) { |
| overwrittenResources.add(deleted[j]); |
| } |
| } |
| } else { |
| if (existing != null) { |
| if (source.isLinked() == existing.isLinked()) { |
| // Record the original path |
| reverseDestinations.add(source.getFullPath()); |
| overwrittenResources.add(copyOverExistingResource( |
| source, existing, new SubProgressMonitor( |
| monitor, 1), uiInfo, true)); |
| resourcesAtDestination.add(existing); |
| } else { |
| // Moving a linked resource over unlinked or vice |
| // versa. Can't use setContents here. Fixes bug 28772. |
| ResourceDescription[] deleted = delete( |
| new IResource[] { existing }, |
| new SubProgressMonitor(monitor, 0), uiInfo, |
| false); |
| reverseDestinations.add(source.getFullPath()); |
| source.move(destinationPath, IResource.SHALLOW |
| | IResource.KEEP_HISTORY, |
| new SubProgressMonitor(monitor, 1)); |
| // Record the resource at its destination |
| resourcesAtDestination.add(getWorkspace().getRoot() |
| .findMember(destinationPath)); |
| for (int j = 0; j < deleted.length; j++) { |
| overwrittenResources.add(deleted[j]); |
| } |
| } |
| } else { |
| // No resources are being overwritten. |
| // First record the source path |
| reverseDestinations.add(source.getFullPath()); |
| // ensure the destination path exists |
| IPath parentPath = destination; |
| if (pathIncludesName) { |
| parentPath = destination.removeLastSegments(1); |
| } |
| |
| IContainer generatedParent = generateContainers(parentPath); |
| source.move(destinationPath, IResource.SHALLOW |
| | IResource.KEEP_HISTORY, new SubProgressMonitor( |
| monitor, 1)); |
| // Record the move. If we had to generate a parent |
| // folder, that should be recorded as part of the copy |
| if (generatedParent == null) { |
| resourcesAtDestination.add(getWorkspace().getRoot() |
| .findMember(destinationPath)); |
| } else { |
| resourcesAtDestination.add(generatedParent); |
| } |
| } |
| |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| } |
| } |
| monitor.done(); |
| return (ResourceDescription[]) overwrittenResources |
| .toArray(new ResourceDescription[overwrittenResources.size()]); |
| |
| } |
| |
| /** |
| * Recreate the resources from the specified resource descriptions. |
| * |
| * @param resourcesToRecreate |
| * the ResourceDescriptions describing resources to be recreated |
| * @param monitor |
| * the progress monitor used to show progress |
| * @param uiInfo |
| * the IAdaptable (or <code>null</code>) provided by the |
| * caller in order to supply UI information for prompting the |
| * user if necessary. When this parameter is not |
| * <code>null</code>, it contains an adapter for the |
| * org.eclipse.swt.widgets.Shell.class |
| * @return an array of resources that were created |
| * @throws CoreException |
| * propagates any CoreExceptions thrown from the resources API |
| */ |
| static IResource[] recreate(ResourceDescription[] resourcesToRecreate, |
| IProgressMonitor monitor, IAdaptable uiInfo) throws CoreException { |
| final List exceptions = new ArrayList(); |
| IResource[] resourcesToReturn = new IResource[resourcesToRecreate.length]; |
| monitor.beginTask("", resourcesToRecreate.length); //$NON-NLS-1$ |
| monitor |
| .setTaskName(UndoMessages.AbstractResourcesOperation_CreateResourcesProgress); |
| try { |
| for (int i = 0; i < resourcesToRecreate.length; ++i) { |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| try { |
| resourcesToReturn[i] = resourcesToRecreate[i] |
| .createResource(new SubProgressMonitor(monitor, 1)); |
| } catch (CoreException e) { |
| exceptions.add(e); |
| } |
| } |
| IStatus result = WorkspaceUndoUtil.createResult(exceptions); |
| if (!result.isOK()) { |
| throw new CoreException(result); |
| } |
| } finally { |
| monitor.done(); |
| } |
| return resourcesToReturn; |
| } |
| |
| /** |
| * Delete the specified resources, returning a resource description that can |
| * be used to restore it. |
| * |
| * @param resourceToDelete |
| * the resource to be deleted |
| * @param monitor |
| * the progress monitor to use to show the operation's progress |
| * @param uiInfo |
| * the IAdaptable (or <code>null</code>) provided by the |
| * caller in order to supply UI information for prompting the |
| * user if necessary. When this parameter is not |
| * <code>null</code>, it contains an adapter for the |
| * org.eclipse.swt.widgets.Shell.class |
| * @param forceOutOfSyncDelete |
| * a boolean indicating whether a resource should be deleted even |
| * if it is out of sync with the file system |
| * @param deleteContent |
| * a boolean indicating whether project content should be deleted |
| * when a project resource is to be deleted |
| * @return a ResourceDescription that can be used to restore the deleted |
| * resource. |
| * @throws CoreException |
| * propagates any CoreExceptions thrown from the resources API |
| */ |
| static ResourceDescription delete(IResource resourceToDelete, |
| IProgressMonitor monitor, IAdaptable uiInfo, |
| boolean forceOutOfSyncDelete, boolean deleteContent) |
| throws CoreException { |
| ResourceDescription resourceDescription = ResourceDescription |
| .fromResource(resourceToDelete); |
| if (resourceToDelete.getType() == IResource.PROJECT) { |
| // it is a project |
| monitor |
| .setTaskName(UndoMessages.AbstractResourcesOperation_DeleteResourcesProgress); |
| IProject project = (IProject) resourceToDelete; |
| project.delete(deleteContent, forceOutOfSyncDelete, monitor); |
| } else { |
| // if it's not a project, just delete it |
| monitor.beginTask("", 2); //$NON-NLS-1$ |
| monitor |
| .setTaskName(UndoMessages.AbstractResourcesOperation_DeleteResourcesProgress); |
| int updateFlags; |
| if (forceOutOfSyncDelete) { |
| updateFlags = IResource.KEEP_HISTORY | IResource.FORCE; |
| } else { |
| updateFlags = IResource.KEEP_HISTORY; |
| } |
| resourceToDelete.delete(updateFlags, new SubProgressMonitor( |
| monitor, 1)); |
| resourceDescription.recordStateFromHistory(resourceToDelete, |
| new SubProgressMonitor(monitor, 1)); |
| monitor.done(); |
| } |
| |
| return resourceDescription; |
| } |
| |
| /* |
| * Copy the content of the specified resource to the existing resource, |
| * returning a ResourceDescription that can be used to restore the original |
| * content. Do nothing if the resources are not files. |
| */ |
| private static ResourceDescription copyOverExistingResource( |
| IResource source, IResource existing, IProgressMonitor monitor, |
| IAdaptable uiInfo, boolean deleteSourceFile) throws CoreException { |
| if (!(source instanceof IFile && existing instanceof IFile)) { |
| return null; |
| } |
| IFile file = (IFile) source; |
| IFile existingFile = (IFile) existing; |
| monitor |
| .beginTask( |
| UndoMessages.AbstractResourcesOperation_CopyingResourcesProgress, |
| 3); |
| if (file != null && existingFile != null) { |
| if (validateEdit(file, existingFile, getShell(uiInfo))) { |
| // Remember the state of the existing file so it can be |
| // restored. |
| FileDescription fileDescription = new FileDescription( |
| existingFile); |
| // Reset the contents to that of the file being moved |
| existingFile.setContents(file.getContents(), |
| IResource.KEEP_HISTORY, new SubProgressMonitor(monitor, |
| 1)); |
| fileDescription.recordStateFromHistory(existingFile, |
| new SubProgressMonitor(monitor, 1)); |
| // Now delete the source file if requested |
| // We don't need to remember anything about it, because |
| // any undo involving this operation will move the original |
| // content back to it. |
| if (deleteSourceFile) { |
| file.delete(IResource.KEEP_HISTORY, new SubProgressMonitor( |
| monitor, 1)); |
| } |
| monitor.done(); |
| return fileDescription; |
| } |
| } |
| monitor.done(); |
| return null; |
| } |
| |
| /* |
| * Check for existence of the specified path and generate any containers |
| * that do not yet exist. Return any generated containers, or null if no |
| * container had to be generated. |
| */ |
| private static IContainer generateContainers(IPath path) |
| throws CoreException { |
| IResource container; |
| if (path.segmentCount() == 0) { |
| // nothing to generate |
| return null; |
| } |
| container = getWorkspaceRoot().findMember(path); |
| // Nothing to generate because container exists |
| if (container != null) { |
| return null; |
| } |
| |
| // Now make a non-existent handle representing the desired container |
| if (path.segmentCount() == 1) { |
| container = ResourcesPlugin.getWorkspace().getRoot().getProject( |
| path.segment(0)); |
| } else { |
| container = ResourcesPlugin.getWorkspace().getRoot() |
| .getFolder(path); |
| } |
| ContainerDescription containerDescription = ContainerDescription |
| .fromContainer((IContainer) container); |
| container = containerDescription.createResourceHandle(); |
| containerDescription.createExistentResourceFromHandle(container, |
| new NullProgressMonitor()); |
| return (IContainer) container; |
| } |
| |
| /* |
| * Ask the user whether the given resource should be deleted despite being |
| * out of sync with the file system. |
| * |
| * Return one of the IDialogConstants constants indicating which of the Yes, |
| * Yes to All, No, Cancel options has been selected by the user. |
| */ |
| private static int queryDeleteOutOfSync(IResource resource, |
| IAdaptable uiInfo) { |
| Shell shell = getShell(uiInfo); |
| final MessageDialog dialog = new MessageDialog( |
| shell, |
| UndoMessages.AbstractResourcesOperation_deletionMessageTitle, |
| null, |
| NLS |
| .bind( |
| UndoMessages.AbstractResourcesOperation_outOfSyncQuestion, |
| resource.getName()), MessageDialog.QUESTION, |
| new String[] { IDialogConstants.YES_LABEL, |
| IDialogConstants.YES_TO_ALL_LABEL, |
| IDialogConstants.NO_LABEL, |
| IDialogConstants.CANCEL_LABEL }, 0); |
| shell.getDisplay().syncExec(new Runnable() { |
| public void run() { |
| dialog.open(); |
| } |
| }); |
| int result = dialog.getReturnCode(); |
| if (result == 0) { |
| return IDialogConstants.YES_ID; |
| } |
| if (result == 1) { |
| return IDialogConstants.YES_TO_ALL_ID; |
| } |
| if (result == 2) { |
| return IDialogConstants.NO_ID; |
| } |
| return IDialogConstants.CANCEL_ID; |
| } |
| |
| /* |
| * Creates and return a result status appropriate for the given list of |
| * exceptions. |
| */ |
| private static IStatus createResult(List exceptions) { |
| if (exceptions.isEmpty()) { |
| return Status.OK_STATUS; |
| } |
| final int exceptionCount = exceptions.size(); |
| if (exceptionCount == 1) { |
| return ((CoreException) exceptions.get(0)).getStatus(); |
| } |
| CoreException[] children = (CoreException[]) exceptions |
| .toArray(new CoreException[exceptionCount]); |
| boolean outOfSync = false; |
| for (int i = 0; i < children.length; i++) { |
| if (children[i].getStatus().getCode() == IResourceStatus.OUT_OF_SYNC_LOCAL) { |
| outOfSync = true; |
| break; |
| } |
| } |
| String title = outOfSync ? UndoMessages.AbstractResourcesOperation_outOfSyncError |
| : UndoMessages.AbstractResourcesOperation_deletionExceptionMessage; |
| final MultiStatus multi = new MultiStatus( |
| IDEWorkbenchPlugin.IDE_WORKBENCH, 0, title, null); |
| for (int i = 0; i < exceptionCount; i++) { |
| CoreException exception = children[i]; |
| IStatus status = exception.getStatus(); |
| multi.add(new Status(status.getSeverity(), status.getPlugin(), |
| status.getCode(), status.getMessage(), exception)); |
| } |
| return multi; |
| } |
| |
| /* |
| * Return the workspace. |
| */ |
| private static IWorkspace getWorkspace() { |
| return ResourcesPlugin.getWorkspace(); |
| } |
| |
| /* |
| * Return the workspace root. |
| */ |
| private static IWorkspaceRoot getWorkspaceRoot() { |
| return getWorkspace().getRoot(); |
| } |
| |
| /* |
| * Validate the destination file if it is read-only and additionally the |
| * source file if both are read-only. Returns true if both files could be |
| * made writeable. |
| */ |
| private static boolean validateEdit(IFile source, IFile destination, |
| Shell shell) { |
| if (destination.isReadOnly()) { |
| IWorkspace workspace = WorkspaceUndoUtil.getWorkspace(); |
| IStatus status; |
| if (source.isReadOnly()) { |
| status = workspace.validateEdit(new IFile[] { source, |
| destination }, shell); |
| } else { |
| status = workspace.validateEdit(new IFile[] { destination }, |
| shell); |
| } |
| return status.isOK(); |
| } |
| return true; |
| } |
| |
| /** |
| * Return the shell described by the specified adaptable, or the active |
| * shell if no shell has been specified in the adaptable. |
| * |
| * @param uiInfo |
| * the IAdaptable (or <code>null</code>) provided by the |
| * caller in order to supply UI information for prompting the |
| * user if necessary. When this parameter is not |
| * <code>null</code>, it contains an adapter for the |
| * org.eclipse.swt.widgets.Shell.class |
| * |
| * @return the Shell that can be used to show information |
| */ |
| public static Shell getShell(IAdaptable uiInfo) { |
| if (uiInfo != null) { |
| Shell shell = (Shell) uiInfo.getAdapter(Shell.class); |
| if (shell != null) { |
| return shell; |
| } |
| } |
| return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); |
| } |
| } |