blob: 75806e654bdf53f1e459bb67c75d2ec34c5041e5 [file] [log] [blame]
/*******************************************************************************
* 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 org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.internal.ide.undo.UndoMessages;
/**
* An AbstractCopyOrMoveResourcesOperation represents an undoable operation for
* moving or copying one or more resources in the workspace. Clients may call
* the public API from a background thread.
*
* This class is not intended to be subclassed by clients.
*
* @since 3.3
*
*/
abstract class AbstractCopyOrMoveResourcesOperation extends
AbstractResourcesOperation {
// Used when there are different destination names for each resource
protected IPath[] destinationPaths = null;
// Used when all resources are going to the same container (no name changes)
protected IPath destination = null;
/**
* Create an AbstractCopyOrMoveResourcesOperation that moves or copies all
* of the specified resources to the specified paths. The destination paths
* must include the names of the resources at their new location.
*
* @param resources
* the resources to be moved or copied. May not contain null
* resources, or resources that are descendants of already
* included resources.
* @param destinationPaths
* the destination paths for the resources, including the name to
* be assigned to the resource at its new location. May not contain
* null paths, and must be the same length as the resources array.
* @param label
* the label of the operation
*
*/
AbstractCopyOrMoveResourcesOperation(IResource[] resources,
IPath[] destinationPaths, String label) {
super(resources, label);
// Check for null arguments
if (this.resources == null || destinationPaths == null)
throw new IllegalArgumentException("The resource and destination paths may not be null"); //$NON-NLS-1$
// Special case to flag descendants. Note this would fail on the next check
// anyway, so we are first giving a more specific explanation.
// See bug #176764
if (this.resources.length != resources.length)
throw new IllegalArgumentException("The resource list contained descendants that cannot be moved to separate destination paths"); //$NON-NLS-1$
// Check for destination paths corresponding for each resource
if (this.resources.length != destinationPaths.length) {
throw new IllegalArgumentException("The resource and destination paths must be the same length"); //$NON-NLS-1$
}
for (int i=0; i<this.resources.length; i++) {
if (this.resources[i] == null) {
throw new IllegalArgumentException("The resources array may not contain null resources"); //$NON-NLS-1$
}
if (destinationPaths[i] == null) {
throw new IllegalArgumentException("The destination paths array may not contain null paths"); //$NON-NLS-1$
}
}
this.destinationPaths = destinationPaths;
}
/**
* Create an AbstractCopyOrMoveResourcesOperation that moves or copies all
* of the specified resources to the same target location, using their
* existing names.
*
* @param resources
* the resources to be moved or copied
* @param destinationPath
* the destination path for the resources, not including the name
* of the new resource.
* @param label
* the label of the operation
*/
AbstractCopyOrMoveResourcesOperation(IResource[] resources,
IPath destinationPath, String label) {
super(resources, label);
destination = destinationPath;
}
/**
* Create an AbstractCopyOrMoveResourcesOperation whose destination is not
* yet specified.
*
* @param resources
* the resources to be modified
* @param label
* the label of the operation
*/
AbstractCopyOrMoveResourcesOperation(IResource[] resources, String label) {
super(resources, label);
}
/**
* Compute the status for moving or copying the resources. A status severity
* of <code>OK</code> indicates that the copy or move is likely to be
* successful. A status severity of <code>ERROR</code> indicates that the
* operation is no longer valid. Other status severities are open to
* interpretation by the caller.
*
* Note this method may be called on initial moving or copying of a
* resource, or when a move or copy is undone or redone. Therefore, this
* method should check conditions that can change over the life of the
* operation, such as whether the file to moved or copied exists, and
* whether the target location is still valid. One-time static checks should
* typically be done by the caller so that the user is not continually
* prompted or warned about conditions that were acceptable at the time of
* original execution and do not change over time.
*
* @return the status indicating the projected outcome of moving or copying
* the resources.
*/
protected IStatus computeMoveOrCopyStatus() {
// Check for error conditions first so that we do not prompt the user
// on warnings that eventually will not matter anyway.
if (resources == null) {
markInvalid();
return getErrorStatus(UndoMessages.AbstractResourcesOperation_NotEnoughInfo);
}
for (int i = 0; i < resources.length; i++) {
IResource resource = resources[i];
// Does the resource still exist?
if (!resource.exists()) {
markInvalid();
return getErrorStatus(UndoMessages.AbstractCopyOrMoveResourcesOperation_ResourceDoesNotExist);
}
// Are we really trying to move it to a different name?
if (!isDestinationPathValid(resource, i)) {
markInvalid();
return getErrorStatus(UndoMessages.AbstractCopyOrMoveResourcesOperation_SameNameOrLocation);
}
// Is the proposed name valid?
IStatus status = getWorkspace().validateName(
getProposedName(resource, i), resource.getType());
if (status.getSeverity() == IStatus.ERROR) {
markInvalid();
}
if (!status.isOK()) {
return status;
}
}
return Status.OK_STATUS;
}
/**
* Return the destination path that should be used to move or copy the
* specified resource. This path is relative to the workspace.
*
* @param resource
* the resource being moved or copied
* @param index
* the integer index of the resource in the resource array
* @return the path specifying the destination for the resource
*/
protected IPath getDestinationPath(IResource resource, int index) {
if (destinationPaths != null) {
return destinationPaths[index];
}
return destination.append(resource.getName());
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.ide.undo.AbstractWorkspaceOperation#appendDescriptiveText(java.lang.StringBuffer)
*/
protected void appendDescriptiveText(StringBuffer text) {
super.appendDescriptiveText(text);
text.append(" destination: "); //$NON-NLS-1$
text.append(destination);
text.append(", destinationPaths: "); //$NON-NLS-1$
text.append(destinationPaths);
text.append('\'');
}
/**
* Return a boolean indicating whether the proposed destination path for a
* resource is valid.
*
* @param resource
* the resource whose path is to be checked
* @param index
* the integer index of the resource in the resource array
* @return a boolean indicating whether the destination path is valid
*/
protected boolean isDestinationPathValid(IResource resource, int index) {
return !resource.getFullPath().equals(
getDestinationPath(resource, index));
}
/**
* Return a string indicating the proposed name for the resource
*
* @param resource
* the resource whose path is to be checked
* @param index
* the integer index of the resource in the resource array
* @return the string name of the resource
*/
protected String getProposedName(IResource resource, int index) {
return getDestinationPath(resource, index).lastSegment();
}
/*
* (non-Javadoc)
*
* Map execution to move status.
*
* @see org.eclipse.ui.ide.undo.AbstractWorkspaceOperation#computeExecutionStatus(org.eclipse.core.runtime.IProgressMonitor)
*/
public IStatus computeExecutionStatus(IProgressMonitor monitor) {
IStatus status = super.computeExecutionStatus(monitor);
if (status.isOK()) {
status = computeMoveOrCopyStatus();
}
return status;
}
/*
* (non-Javadoc)
*
* Map redo to move status.
*
* @see org.eclipse.ui.ide.undo.AbstractWorkspaceOperation#computeRedoableStatus(org.eclipse.core.runtime.IProgressMonitor)
*/
public IStatus computeRedoableStatus(IProgressMonitor monitor) {
IStatus status = super.computeRedoableStatus(monitor);
if (status.isOK()) {
status = computeMoveOrCopyStatus();
}
return status;
}
}