| /******************************************************************************* |
| * Copyright (c) 2006, 2010 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.resources.IResource; |
| import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory; |
| 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.SubProgressMonitor; |
| import org.eclipse.ui.internal.ide.undo.UndoMessages; |
| |
| /** |
| * A MoveResourcesOperation represents an undoable operation for moving one or |
| * more resources in the workspace. Clients may call the public API from a |
| * background thread. |
| * <p> |
| * This operation can track any overwritten resources and restore them when the |
| * move is undone. It is up to clients to determine whether overwrites are |
| * allowed. If a resource should not be overwritten, it should not be included |
| * in this operation. In addition to checking for overwrites, the target |
| * location for the move is assumed to have already been validated by the |
| * client. It will not be revalidated on undo and redo. |
| * </p> |
| * <p> |
| * This class is intended to be instantiated and used by clients. It is not |
| * intended to be subclassed by clients. |
| * <p> |
| * @noextend This class is not intended to be subclassed by clients. |
| * @since 3.3 |
| * |
| */ |
| public class MoveResourcesOperation extends |
| AbstractCopyOrMoveResourcesOperation { |
| |
| IResource[] originalResources; |
| |
| IPath originalDestination; |
| |
| IPath[] originalDestinationPaths; |
| |
| /** |
| * Create a MoveResourcesOperation that moves all of the specified resources |
| * to the same target location, using their existing names. |
| * |
| * @param resources |
| * the resources to be moved |
| * @param destinationPath |
| * the destination path for the resources, not including the name |
| * of the moved resource. |
| * @param label |
| * the label of the operation |
| */ |
| public MoveResourcesOperation(IResource[] resources, IPath destinationPath, |
| String label) { |
| super(resources, destinationPath, label); |
| originalResources = this.resources; |
| originalDestination = this.destination; |
| originalDestinationPaths = this.destinationPaths; |
| } |
| |
| /** |
| * Create a MoveResourcesOperation that moves a single resource to a new |
| * location. The new location includes the name of the resource, so this may |
| * be used for a move/rename operation or a simple move. |
| * |
| * @param resource |
| * the resource to be moved |
| * @param newPath |
| * the new path for the resource, including its desired name. |
| * @param label |
| * the label of the operation |
| */ |
| public MoveResourcesOperation(IResource resource, IPath newPath, |
| String label) { |
| super(new IResource[] { resource }, new IPath[] { newPath }, label); |
| originalResources = this.resources; |
| originalDestination = this.destination; |
| originalDestinationPaths = this.destinationPaths; |
| } |
| |
| /* |
| * Map execute to moving the resources |
| */ |
| @Override |
| protected void doExecute(IProgressMonitor monitor, IAdaptable uiInfo) |
| throws CoreException { |
| move(monitor, uiInfo); |
| } |
| |
| /** |
| * Move any known resources according to the destination parameters known by |
| * this operation. Store enough information to undo and redo the operation. |
| * |
| * @param monitor |
| * the progress monitor to use for the operation |
| * @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 |
| * @throws CoreException |
| * propagates any CoreExceptions thrown from the resources API |
| */ |
| protected void move(IProgressMonitor monitor, IAdaptable uiInfo) |
| throws CoreException { |
| |
| monitor.beginTask("", 2000); //$NON-NLS-1$ |
| monitor |
| .setTaskName(UndoMessages.AbstractResourcesOperation_MovingResources); |
| List resourcesAtDestination = new ArrayList(); |
| List undoDestinationPaths = new ArrayList(); |
| List overwrittenResources = new ArrayList(); |
| |
| for (int i = 0; i < resources.length; i++) { |
| // Move the resources and record the overwrites that would |
| // be restored if this operation were reversed |
| ResourceDescription[] overwrites; |
| overwrites = WorkspaceUndoUtil.move( |
| new IResource[] { resources[i] }, getDestinationPath( |
| resources[i], i), resourcesAtDestination, |
| undoDestinationPaths, new SubProgressMonitor(monitor, |
| 1000 / resources.length), uiInfo, true); |
| |
| // Accumulate the overwrites into the full list |
| for (int j = 0; j < overwrites.length; j++) { |
| overwrittenResources.add(overwrites[j]); |
| } |
| } |
| |
| // Are there any previously overwritten resources to restore now? |
| if (resourceDescriptions != null) { |
| for (int i = 0; i < resourceDescriptions.length; i++) { |
| if (resourceDescriptions[i] != null) { |
| resourceDescriptions[i] |
| .createResource(new SubProgressMonitor(monitor, |
| 1000 / resourceDescriptions.length)); |
| } |
| } |
| } |
| |
| // Reset resource descriptions to the just overwritten resources |
| setResourceDescriptions((ResourceDescription[]) overwrittenResources |
| .toArray(new ResourceDescription[overwrittenResources.size()])); |
| |
| // Reset the target resources to refer to the resources in their new |
| // location. |
| setTargetResources((IResource[]) resourcesAtDestination |
| .toArray(new IResource[resourcesAtDestination.size()])); |
| // Reset the destination paths that correspond to these resources |
| destinationPaths = (IPath[]) undoDestinationPaths |
| .toArray(new IPath[undoDestinationPaths.size()]); |
| destination = null; |
| |
| monitor.done(); |
| } |
| |
| /* |
| * Map undo to moving the resources. |
| */ |
| @Override |
| protected void doUndo(IProgressMonitor monitor, IAdaptable uiInfo) |
| throws CoreException { |
| // We've recorded the original moves atomically, so perform the move |
| move(monitor, uiInfo); |
| // Now reset everything back to the way it was originally. |
| // If we don't do this, the move will be "precisely reversed." |
| // For example, if we merged a folder by moving certain files, |
| // we want redo to redo the folder merge, rather than remembering |
| // only the files that were originally merged. This makes us more |
| // adaptable to changes in the target. |
| setTargetResources(originalResources); |
| this.resourceDescriptions = new ResourceDescription[0]; |
| this.destination = originalDestination; |
| this.destinationPaths = originalDestinationPaths; |
| } |
| |
| @Override |
| protected boolean updateResourceChangeDescriptionFactory( |
| IResourceChangeDescriptionFactory factory, int operation) { |
| for (int i = 0; i < resources.length; i++) { |
| IResource resource = resources[i]; |
| factory.move(resource, getDestinationPath(resource, i)); |
| } |
| return true; |
| } |
| |
| /* |
| * Map undo to move status. |
| */ |
| @Override |
| public IStatus computeUndoableStatus(IProgressMonitor monitor) { |
| IStatus status = super.computeUndoableStatus(monitor); |
| if (status.isOK()) { |
| status = computeMoveOrCopyStatus(); |
| } |
| return status; |
| } |
| } |