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 java.util.ArrayList;
013: import java.util.List;
014:
015: import org.eclipse.core.resources.IResource;
016: import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
017: import org.eclipse.core.runtime.CoreException;
018: import org.eclipse.core.runtime.IAdaptable;
019: import org.eclipse.core.runtime.IPath;
020: import org.eclipse.core.runtime.IProgressMonitor;
021: import org.eclipse.core.runtime.IStatus;
022: import org.eclipse.core.runtime.SubProgressMonitor;
023: import org.eclipse.ui.internal.ide.undo.UndoMessages;
024:
025: /**
026: * A CopyResourcesOperation represents an undoable operation for copying one or
027: * more resources in the workspace. Clients may call the public API from a
028: * background thread.
029: *
030: * This operation can track any overwritten resources and restore them when the
031: * copy is undone. It is up to clients to determine whether overwrites are
032: * allowed. If a resource should not be overwritten, it should not be included
033: * in this operation. In addition to checking for overwrites, the target
034: * location for the copy is assumed to have already been validated by the
035: * client. It will not be revalidated on undo and redo.
036: *
037: * This class is intended to be instantiated and used by clients. It is not
038: * intended to be subclassed by clients.
039: *
040: * @since 3.3
041: *
042: */
043: public class CopyResourcesOperation extends
044: AbstractCopyOrMoveResourcesOperation {
045:
046: IResource[] originalResources;
047:
048: ResourceDescription[] snapshotResourceDescriptions;
049:
050: /**
051: * Create a CopyResourcesOperation that copies a single resource to a new
052: * location. The new location includes the name of the copy.
053: *
054: * @param resource
055: * the resource to be copied
056: * @param newPath
057: * the new workspace-relative path for the copy, including its
058: * desired name.
059: * @param label
060: * the label of the operation
061: */
062: public CopyResourcesOperation(IResource resource, IPath newPath,
063: String label) {
064: super (new IResource[] { resource }, new IPath[] { newPath },
065: label);
066: setOriginalResources(new IResource[] { resource });
067: }
068:
069: /**
070: * Create a CopyResourcesOperation that copies all of the specified
071: * resources to a single target location. The original resource name will be
072: * used when copied to the new location.
073: *
074: * @param resources
075: * the resources to be copied
076: * @param destinationPath
077: * the workspace-relative destination path for the copied
078: * resource.
079: * @param label
080: * the label of the operation
081: */
082: public CopyResourcesOperation(IResource[] resources,
083: IPath destinationPath, String label) {
084: super (resources, destinationPath, label);
085: setOriginalResources(this .resources);
086: }
087:
088: /**
089: * Create a CopyResourcesOperation that copies each of the specified
090: * resources to its corresponding destination path in the destination path
091: * array. The resource name for the target is included in the corresponding
092: * destination path.
093: *
094: * @param resources
095: * the resources to be copied. Must not contain null resources.
096: * @param destinationPaths
097: * a workspace-relative destination path for each copied
098: * resource, which includes the name of the resource at the new
099: * destination. Must be the same length as the resources array,
100: * and may not contain null paths.
101: * @param label
102: * the label of the operation
103: */
104: public CopyResourcesOperation(IResource[] resources,
105: IPath[] destinationPaths, String label) {
106: super (resources, destinationPaths, label);
107: setOriginalResources(this .resources);
108: }
109:
110: /*
111: * (non-Javadoc)
112: *
113: * This implementation copies the resources.
114: *
115: * @see org.eclipse.ui.ide.undo.AbstractWorkspaceOperation#doExecute(org.eclipse.core.runtime.IProgressMonitor,
116: * org.eclipse.core.runtime.IAdaptable)
117: */
118: protected void doExecute(IProgressMonitor monitor, IAdaptable uiInfo)
119: throws CoreException {
120: copy(monitor, uiInfo);
121: }
122:
123: /**
124: * Move or copy any known resources according to the destination parameters
125: * known by this operation. Store enough information to undo and redo the
126: * operation.
127: *
128: * @param monitor
129: * the progress monitor to use for the operation
130: * @param uiInfo
131: * the IAdaptable (or <code>null</code>) provided by the
132: * caller in order to supply UI information for prompting the
133: * user if necessary. When this parameter is not
134: * <code>null</code>, it contains an adapter for the
135: * org.eclipse.swt.widgets.Shell.class
136: * @throws CoreException
137: * propagates any CoreExceptions thrown from the resources API
138: */
139: protected void copy(IProgressMonitor monitor, IAdaptable uiInfo)
140: throws CoreException {
141:
142: monitor.beginTask("", 2000); //$NON-NLS-1$
143: monitor
144: .setTaskName(UndoMessages.AbstractResourcesOperation_CopyingResourcesProgress);
145: List resourcesAtDestination = new ArrayList();
146: List overwrittenResources = new ArrayList();
147:
148: for (int i = 0; i < resources.length; i++) {
149: // Copy the resources and record the overwrites that would
150: // be restored if this operation were reversed
151: ResourceDescription[] overwrites;
152: overwrites = WorkspaceUndoUtil.copy(
153: new IResource[] { resources[i] },
154: getDestinationPath(resources[i], i),
155: resourcesAtDestination, new SubProgressMonitor(
156: monitor, 1000 / resources.length), uiInfo,
157: true);
158: // Accumulate the overwrites into the full list
159: for (int j = 0; j < overwrites.length; j++) {
160: overwrittenResources.add(overwrites[j]);
161: }
162: }
163:
164: // Are there any previously overwritten resources to restore now?
165: if (resourceDescriptions != null) {
166: for (int i = 0; i < resourceDescriptions.length; i++) {
167: if (resourceDescriptions[i] != null) {
168: resourceDescriptions[i]
169: .createResource(new SubProgressMonitor(
170: monitor,
171: 1000 / resourceDescriptions.length));
172: }
173: }
174: }
175:
176: // Reset resource descriptions to the just overwritten resources
177: setResourceDescriptions((ResourceDescription[]) overwrittenResources
178: .toArray(new ResourceDescription[overwrittenResources
179: .size()]));
180:
181: // Reset the target resources to refer to the resources in their new
182: // location.
183: setTargetResources((IResource[]) resourcesAtDestination
184: .toArray(new IResource[resourcesAtDestination.size()]));
185: monitor.done();
186: }
187:
188: /*
189: * (non-Javadoc)
190: *
191: * This implementation deletes the previously made copies and restores any
192: * resources that were overwritten by the copy.
193: *
194: * @see org.eclipse.ui.ide.undo.AbstractWorkspaceOperation#doUndo(org.eclipse.core.runtime.IProgressMonitor,
195: * org.eclipse.core.runtime.IAdaptable)
196: */
197: protected void doUndo(IProgressMonitor monitor, IAdaptable uiInfo)
198: throws CoreException {
199: monitor.beginTask("", 2); //$NON-NLS-1$
200: monitor
201: .setTaskName(UndoMessages.AbstractResourcesOperation_CopyingResourcesProgress);
202: // undoing a copy is first deleting the copied resources...
203: WorkspaceUndoUtil.delete(resources, new SubProgressMonitor(
204: monitor, 1), uiInfo, true);
205: // then restoring any overwritten by the previous copy...
206: WorkspaceUndoUtil.recreate(resourceDescriptions,
207: new SubProgressMonitor(monitor, 1), uiInfo);
208: setResourceDescriptions(new ResourceDescription[0]);
209: // then setting the target resources back to the original ones.
210: // Note that the destination paths never changed since they
211: // are not used during undo.
212: setTargetResources(originalResources);
213: monitor.done();
214: }
215:
216: /*
217: * (non-Javadoc)
218: *
219: * @see org.eclipse.ui.ide.undo.AbstractWorkspaceOperation#updateResourceChangeDescriptionFactory(org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory,
220: * int)
221: */
222: protected boolean updateResourceChangeDescriptionFactory(
223: IResourceChangeDescriptionFactory factory, int operation) {
224: boolean update = false;
225: if (operation == UNDO) {
226: for (int i = 0; i < resources.length; i++) {
227: update = true;
228: IResource resource = resources[i];
229: factory.delete(resource);
230: }
231: for (int i = 0; i < resourceDescriptions.length; i++) {
232: update = true;
233: IResource resource = resourceDescriptions[i]
234: .createResourceHandle();
235: factory.create(resource);
236: }
237: } else {
238: for (int i = 0; i < resources.length; i++) {
239: update = true;
240: IResource resource = resources[i];
241: factory.copy(resource, getDestinationPath(resource, i));
242: }
243: }
244: return update;
245: }
246:
247: /*
248: * (non-Javadoc)
249: *
250: * This implementation computes the ability to delete the original copy and
251: * restore any overwritten resources.
252: *
253: * @see org.eclipse.ui.ide.undo.AbstractWorkspaceOperation#computeUndoableStatus(org.eclipse.core.runtime.IProgressMonitor)
254: */
255: public IStatus computeUndoableStatus(IProgressMonitor monitor) {
256: IStatus status = super .computeUndoableStatus(monitor);
257: if (!status.isOK()) {
258: return status;
259: }
260: // If the originals no longer exist, we do not want to attempt to
261: // undo the copy which involves deleting the copies. They may be all we
262: // have left.
263: if (originalResources == null) {
264: markInvalid();
265: return getErrorStatus(UndoMessages.CopyResourcesOperation_NotAllowedDueToDataLoss);
266: }
267: for (int i = 0; i < snapshotResourceDescriptions.length; i++) {
268: if (!snapshotResourceDescriptions[i].verifyExistence(true)) {
269: markInvalid();
270: return getErrorStatus(UndoMessages.CopyResourcesOperation_NotAllowedDueToDataLoss);
271: }
272: }
273: // undoing a copy means deleting the copy that was made
274: if (status.isOK()) {
275: status = computeDeleteStatus();
276: }
277: // and if there were resources overwritten by the copy, can we still
278: // recreate them?
279: if (status.isOK() && resourceDescriptions != null
280: && resourceDescriptions.length > 0) {
281: status = computeCreateStatus(true);
282: }
283:
284: return status;
285: }
286:
287: /*
288: * Record the original resources, including a resource description to
289: * describe it. This is so we can make sure the original resources and their
290: * subtrees are intact before allowing a copy to be undone.
291: */
292: private void setOriginalResources(IResource[] originals) {
293: originalResources = originals;
294: snapshotResourceDescriptions = new ResourceDescription[originals.length];
295: for (int i = 0; i < originals.length; i++) {
296: snapshotResourceDescriptions[i] = ResourceDescription
297: .fromResource(originals[i]);
298: }
299: }
300: }
|