blob: 8c4617c218efa9a2e608e2aa2df4c2cfcfb950d5 [file] [log] [blame]
/*******************************************************************************
* 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 CopyResourcesOperation represents an undoable operation for copying 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
* copy 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 copy 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 CopyResourcesOperation extends
AbstractCopyOrMoveResourcesOperation {
IResource[] originalResources;
ResourceDescription[] snapshotResourceDescriptions;
/**
* Create a CopyResourcesOperation that copies a single resource to a new
* location. The new location includes the name of the copy.
*
* @param resource
* the resource to be copied
* @param newPath
* the new workspace-relative path for the copy, including its
* desired name.
* @param label
* the label of the operation
*/
public CopyResourcesOperation(IResource resource, IPath newPath,
String label) {
super(new IResource[] { resource }, new IPath[] { newPath }, label);
setOriginalResources(new IResource[] { resource });
}
/**
* Create a CopyResourcesOperation that copies all of the specified
* resources to a single target location. The original resource name will be
* used when copied to the new location.
*
* @param resources
* the resources to be copied
* @param destinationPath
* the workspace-relative destination path for the copied
* resource.
* @param label
* the label of the operation
*/
public CopyResourcesOperation(IResource[] resources, IPath destinationPath,
String label) {
super(resources, destinationPath, label);
setOriginalResources(this.resources);
}
/**
* Create a CopyResourcesOperation that copies each of the specified
* resources to its corresponding destination path in the destination path
* array. The resource name for the target is included in the corresponding
* destination path.
*
* @param resources
* the resources to be copied. Must not contain null resources.
* @param destinationPaths
* a workspace-relative destination path for each copied
* resource, which includes the name of the resource at the new
* destination. Must be the same length as the resources array,
* and may not contain null paths.
* @param label
* the label of the operation
*/
public CopyResourcesOperation(IResource[] resources,
IPath[] destinationPaths, String label) {
super(resources, destinationPaths, label);
setOriginalResources(this.resources);
}
/*
* This implementation copies the resources.
*/
@Override
protected void doExecute(IProgressMonitor monitor, IAdaptable uiInfo)
throws CoreException {
copy(monitor, uiInfo);
}
/**
* Move or copy 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 copy(IProgressMonitor monitor, IAdaptable uiInfo)
throws CoreException {
monitor.beginTask("", 2000); //$NON-NLS-1$
monitor
.setTaskName(UndoMessages.AbstractResourcesOperation_CopyingResourcesProgress);
List resourcesAtDestination = new ArrayList();
List overwrittenResources = new ArrayList();
for (int i = 0; i < resources.length; i++) {
// Copy the resources and record the overwrites that would
// be restored if this operation were reversed
ResourceDescription[] overwrites;
overwrites = WorkspaceUndoUtil.copy(
new IResource[] { resources[i] }, getDestinationPath(
resources[i], i), resourcesAtDestination,
new SubProgressMonitor(monitor, 1000 / resources.length),
uiInfo, true, fCreateGroups, fCreateLinks,
fRelativeToVariable);
// 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()]));
monitor.done();
}
/*
* This implementation deletes the previously made copies and restores any
* resources that were overwritten by the copy.
*/
@Override
protected void doUndo(IProgressMonitor monitor, IAdaptable uiInfo)
throws CoreException {
monitor.beginTask("", 2); //$NON-NLS-1$
monitor
.setTaskName(UndoMessages.AbstractResourcesOperation_CopyingResourcesProgress);
// undoing a copy is first deleting the copied resources...
WorkspaceUndoUtil.delete(resources, new SubProgressMonitor(monitor, 1),
uiInfo, true);
// then restoring any overwritten by the previous copy...
WorkspaceUndoUtil.recreate(resourceDescriptions,
new SubProgressMonitor(monitor, 1), uiInfo);
setResourceDescriptions(new ResourceDescription[0]);
// then setting the target resources back to the original ones.
// Note that the destination paths never changed since they
// are not used during undo.
setTargetResources(originalResources);
monitor.done();
}
@Override
protected boolean updateResourceChangeDescriptionFactory(
IResourceChangeDescriptionFactory factory, int operation) {
boolean update = false;
if (operation == UNDO) {
for (int i = 0; i < resources.length; i++) {
update = true;
IResource resource = resources[i];
factory.delete(resource);
}
for (int i = 0; i < resourceDescriptions.length; i++) {
if (resourceDescriptions[i] != null) {
update = true;
IResource resource = resourceDescriptions[i]
.createResourceHandle();
factory.create(resource);
}
}
} else {
for (int i = 0; i < resources.length; i++) {
update = true;
IResource resource = resources[i];
factory.copy(resource, getDestinationPath(resource, i));
}
}
return update;
}
/*
* This implementation computes the ability to delete the original copy and
* restore any overwritten resources.
*/
@Override
public IStatus computeUndoableStatus(IProgressMonitor monitor) {
IStatus status = super.computeUndoableStatus(monitor);
if (!status.isOK()) {
return status;
}
// If the originals no longer exist, we do not want to attempt to
// undo the copy which involves deleting the copies. They may be all we
// have left.
if (originalResources == null) {
markInvalid();
return getErrorStatus(UndoMessages.CopyResourcesOperation_NotAllowedDueToDataLoss);
}
for (int i = 0; i < snapshotResourceDescriptions.length; i++) {
if (!snapshotResourceDescriptions[i].verifyExistence(true)) {
markInvalid();
return getErrorStatus(UndoMessages.CopyResourcesOperation_NotAllowedDueToDataLoss);
}
}
// undoing a copy means deleting the copy that was made
if (status.isOK()) {
status = computeDeleteStatus();
}
// and if there were resources overwritten by the copy, can we still
// recreate them?
if (status.isOK() && resourceDescriptions != null
&& resourceDescriptions.length > 0) {
status = computeCreateStatus(true);
}
return status;
}
/*
* Record the original resources, including a resource description to
* describe it. This is so we can make sure the original resources and their
* subtrees are intact before allowing a copy to be undone.
*/
private void setOriginalResources(IResource[] originals) {
originalResources = originals;
snapshotResourceDescriptions = new ResourceDescription[originals.length];
for (int i = 0; i < originals.length; i++) {
snapshotResourceDescriptions[i] = ResourceDescription
.fromResource(originals[i]);
}
}
}