001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.ide.undo;
011:
012: import org.eclipse.core.resources.IResource;
013: import org.eclipse.core.runtime.IPath;
014: import org.eclipse.core.runtime.IProgressMonitor;
015: import org.eclipse.core.runtime.IStatus;
016: import org.eclipse.core.runtime.Status;
017: import org.eclipse.ui.internal.ide.undo.UndoMessages;
018:
019: /**
020: * An AbstractCopyOrMoveResourcesOperation represents an undoable operation for
021: * moving or copying one or more resources in the workspace. Clients may call
022: * the public API from a background thread.
023: *
024: * This class is not intended to be subclassed by clients.
025: *
026: * @since 3.3
027: *
028: */
029: abstract class AbstractCopyOrMoveResourcesOperation extends
030: AbstractResourcesOperation {
031:
032: // Used when there are different destination names for each resource
033: protected IPath[] destinationPaths = null;
034:
035: // Used when all resources are going to the same container (no name changes)
036: protected IPath destination = null;
037:
038: /**
039: * Create an AbstractCopyOrMoveResourcesOperation that moves or copies all
040: * of the specified resources to the specified paths. The destination paths
041: * must include the names of the resources at their new location.
042: *
043: * @param resources
044: * the resources to be moved or copied. May not contain null
045: * resources, or resources that are descendants of already
046: * included resources.
047: * @param destinationPaths
048: * the destination paths for the resources, including the name to
049: * be assigned to the resource at its new location. May not contain
050: * null paths, and must be the same length as the resources array.
051: * @param label
052: * the label of the operation
053: *
054: */
055: AbstractCopyOrMoveResourcesOperation(IResource[] resources,
056: IPath[] destinationPaths, String label) {
057: super (resources, label);
058: // Check for null arguments
059: if (this .resources == null || destinationPaths == null)
060: throw new IllegalArgumentException(
061: "The resource and destination paths may not be null"); //$NON-NLS-1$
062: // Special case to flag descendants. Note this would fail on the next check
063: // anyway, so we are first giving a more specific explanation.
064: // See bug #176764
065: if (this .resources.length != resources.length)
066: throw new IllegalArgumentException(
067: "The resource list contained descendants that cannot be moved to separate destination paths"); //$NON-NLS-1$
068: // Check for destination paths corresponding for each resource
069: if (this .resources.length != destinationPaths.length) {
070: throw new IllegalArgumentException(
071: "The resource and destination paths must be the same length"); //$NON-NLS-1$
072: }
073: for (int i = 0; i < this .resources.length; i++) {
074: if (this .resources[i] == null) {
075: throw new IllegalArgumentException(
076: "The resources array may not contain null resources"); //$NON-NLS-1$
077: }
078: if (destinationPaths[i] == null) {
079: throw new IllegalArgumentException(
080: "The destination paths array may not contain null paths"); //$NON-NLS-1$
081: }
082: }
083: this .destinationPaths = destinationPaths;
084: }
085:
086: /**
087: * Create an AbstractCopyOrMoveResourcesOperation that moves or copies all
088: * of the specified resources to the same target location, using their
089: * existing names.
090: *
091: * @param resources
092: * the resources to be moved or copied
093: * @param destinationPath
094: * the destination path for the resources, not including the name
095: * of the new resource.
096: * @param label
097: * the label of the operation
098: */
099: AbstractCopyOrMoveResourcesOperation(IResource[] resources,
100: IPath destinationPath, String label) {
101: super (resources, label);
102: destination = destinationPath;
103: }
104:
105: /**
106: * Create an AbstractCopyOrMoveResourcesOperation whose destination is not
107: * yet specified.
108: *
109: * @param resources
110: * the resources to be modified
111: * @param label
112: * the label of the operation
113: */
114: AbstractCopyOrMoveResourcesOperation(IResource[] resources,
115: String label) {
116: super (resources, label);
117: }
118:
119: /**
120: * Compute the status for moving or copying the resources. A status severity
121: * of <code>OK</code> indicates that the copy or move is likely to be
122: * successful. A status severity of <code>ERROR</code> indicates that the
123: * operation is no longer valid. Other status severities are open to
124: * interpretation by the caller.
125: *
126: * Note this method may be called on initial moving or copying of a
127: * resource, or when a move or copy is undone or redone. Therefore, this
128: * method should check conditions that can change over the life of the
129: * operation, such as whether the file to moved or copied exists, and
130: * whether the target location is still valid. One-time static checks should
131: * typically be done by the caller so that the user is not continually
132: * prompted or warned about conditions that were acceptable at the time of
133: * original execution and do not change over time.
134: *
135: * @return the status indicating the projected outcome of moving or copying
136: * the resources.
137: */
138: protected IStatus computeMoveOrCopyStatus() {
139: // Check for error conditions first so that we do not prompt the user
140: // on warnings that eventually will not matter anyway.
141: if (resources == null) {
142: markInvalid();
143: return getErrorStatus(UndoMessages.AbstractResourcesOperation_NotEnoughInfo);
144: }
145: for (int i = 0; i < resources.length; i++) {
146: IResource resource = resources[i];
147: // Does the resource still exist?
148: if (!resource.exists()) {
149: markInvalid();
150: return getErrorStatus(UndoMessages.AbstractCopyOrMoveResourcesOperation_ResourceDoesNotExist);
151: }
152:
153: // Are we really trying to move it to a different name?
154: if (!isDestinationPathValid(resource, i)) {
155: markInvalid();
156: return getErrorStatus(UndoMessages.AbstractCopyOrMoveResourcesOperation_SameNameOrLocation);
157: }
158: // Is the proposed name valid?
159: IStatus status = getWorkspace().validateName(
160: getProposedName(resource, i), resource.getType());
161: if (status.getSeverity() == IStatus.ERROR) {
162: markInvalid();
163: }
164: if (!status.isOK()) {
165: return status;
166: }
167: }
168: return Status.OK_STATUS;
169: }
170:
171: /**
172: * Return the destination path that should be used to move or copy the
173: * specified resource. This path is relative to the workspace.
174: *
175: * @param resource
176: * the resource being moved or copied
177: * @param index
178: * the integer index of the resource in the resource array
179: * @return the path specifying the destination for the resource
180: */
181: protected IPath getDestinationPath(IResource resource, int index) {
182: if (destinationPaths != null) {
183: return destinationPaths[index];
184: }
185: return destination.append(resource.getName());
186:
187: }
188:
189: /*
190: * (non-Javadoc)
191: *
192: * @see org.eclipse.ui.ide.undo.AbstractWorkspaceOperation#appendDescriptiveText(java.lang.StringBuffer)
193: */
194: protected void appendDescriptiveText(StringBuffer text) {
195: super .appendDescriptiveText(text);
196: text.append(" destination: "); //$NON-NLS-1$
197: text.append(destination);
198: text.append(", destinationPaths: "); //$NON-NLS-1$
199: text.append(destinationPaths);
200: text.append('\'');
201: }
202:
203: /**
204: * Return a boolean indicating whether the proposed destination path for a
205: * resource is valid.
206: *
207: * @param resource
208: * the resource whose path is to be checked
209: * @param index
210: * the integer index of the resource in the resource array
211: * @return a boolean indicating whether the destination path is valid
212: */
213: protected boolean isDestinationPathValid(IResource resource,
214: int index) {
215: return !resource.getFullPath().equals(
216: getDestinationPath(resource, index));
217: }
218:
219: /**
220: * Return a string indicating the proposed name for the resource
221: *
222: * @param resource
223: * the resource whose path is to be checked
224: * @param index
225: * the integer index of the resource in the resource array
226: * @return the string name of the resource
227: */
228: protected String getProposedName(IResource resource, int index) {
229: return getDestinationPath(resource, index).lastSegment();
230: }
231:
232: /*
233: * (non-Javadoc)
234: *
235: * Map execution to move status.
236: *
237: * @see org.eclipse.ui.ide.undo.AbstractWorkspaceOperation#computeExecutionStatus(org.eclipse.core.runtime.IProgressMonitor)
238: */
239: public IStatus computeExecutionStatus(IProgressMonitor monitor) {
240: IStatus status = super .computeExecutionStatus(monitor);
241: if (status.isOK()) {
242: status = computeMoveOrCopyStatus();
243: }
244: return status;
245: }
246:
247: /*
248: * (non-Javadoc)
249: *
250: * Map redo to move status.
251: *
252: * @see org.eclipse.ui.ide.undo.AbstractWorkspaceOperation#computeRedoableStatus(org.eclipse.core.runtime.IProgressMonitor)
253: */
254: public IStatus computeRedoableStatus(IProgressMonitor monitor) {
255: IStatus status = super.computeRedoableStatus(monitor);
256: if (status.isOK()) {
257: status = computeMoveOrCopyStatus();
258: }
259: return status;
260: }
261: }
|