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.runtime.CoreException;
017: import org.eclipse.core.runtime.IAdaptable;
018: import org.eclipse.core.runtime.IProgressMonitor;
019: import org.eclipse.core.runtime.IStatus;
020: import org.eclipse.core.runtime.Status;
021: import org.eclipse.core.runtime.jobs.ISchedulingRule;
022: import org.eclipse.core.runtime.jobs.MultiRule;
023: import org.eclipse.ui.actions.ReadOnlyStateChecker;
024: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
025: import org.eclipse.ui.internal.ide.undo.UndoMessages;
026:
027: /**
028: * An AbstractResourcesOperation represents an undoable operation that
029: * manipulates resources. It provides implementations for resource rename,
030: * delete, creation, and modification. It also assigns the workspace undo
031: * context as the undo context for operations of this type. Clients may call the
032: * public API from a background thread.
033: *
034: * This class is not intended to be subclassed by clients.
035: *
036: * @since 3.3
037: *
038: */
039: abstract class AbstractResourcesOperation extends
040: AbstractWorkspaceOperation {
041:
042: /*
043: * The array of resource descriptions known by this operation to create or
044: * restore overwritten resources.
045: */
046: protected ResourceDescription[] resourceDescriptions;
047:
048: /*
049: * Return true if the specified subResource is a descendant of the specified
050: * super resource. Used to remove descendants from the resource array when
051: * an operation is requested on a parent and its descendant.
052: */
053: private static boolean isDescendantOf(IResource subResource,
054: IResource super Resource) {
055: return !subResource.equals(super Resource)
056: && super Resource.getFullPath().isPrefixOf(
057: subResource.getFullPath());
058: }
059:
060: /**
061: * Create an Abstract Resources Operation
062: *
063: * @param resources
064: * the resources to be modified
065: * @param label
066: * the label of the operation
067: */
068: AbstractResourcesOperation(IResource[] resources, String label) {
069: super (label);
070: this .addContext(WorkspaceUndoUtil.getWorkspaceUndoContext());
071:
072: setTargetResources(resources);
073: }
074:
075: /**
076: * Create an Abstract Resources Operation
077: *
078: * @param resourceDescriptions
079: * the resourceDescriptions describing resources to be created
080: * @param label
081: * the label of the operation
082: */
083: AbstractResourcesOperation(
084: ResourceDescription[] resourceDescriptions, String label) {
085: super (label);
086: addContext(WorkspaceUndoUtil.getWorkspaceUndoContext());
087: setResourceDescriptions(resourceDescriptions);
088: }
089:
090: /**
091: * Delete any resources known by this operation. Store enough information to
092: * undo and redo the operation.
093: *
094: * @param monitor
095: * the progress monitor to use for the operation
096: * @param uiInfo
097: * the IAdaptable (or <code>null</code>) provided by the
098: * caller in order to supply UI information for prompting the
099: * user if necessary. When this parameter is not
100: * <code>null</code>, it contains an adapter for the
101: * org.eclipse.swt.widgets.Shell.class
102: * @param deleteContent
103: * <code>true</code> if the content of any known projects
104: * should be deleted along with the project. <code>false</code>
105: * if project content should not be deleted.
106: * @throws CoreException
107: * propagates any CoreExceptions thrown from the resources API
108: */
109: protected void delete(IProgressMonitor monitor, IAdaptable uiInfo,
110: boolean deleteContent) throws CoreException {
111: setResourceDescriptions(WorkspaceUndoUtil.delete(resources,
112: monitor, uiInfo, deleteContent));
113: setTargetResources(new IResource[0]);
114: }
115:
116: /**
117: * Recreate any resources known by this operation. Store enough information
118: * to undo and redo the operation.
119: *
120: * @param monitor
121: * the progress monitor to use for the operation
122: * @param uiInfo
123: * the IAdaptable (or <code>null</code>) provided by the
124: * caller in order to supply UI information for prompting the
125: * user if necessary. When this parameter is not
126: * <code>null</code>, it contains an adapter for the
127: * org.eclipse.swt.widgets.Shell.class
128: * @throws CoreException
129: * propagates any CoreExceptions thrown from the resources API
130: */
131: protected void recreate(IProgressMonitor monitor, IAdaptable uiInfo)
132: throws CoreException {
133: setTargetResources(WorkspaceUndoUtil.recreate(
134: resourceDescriptions, monitor, uiInfo));
135: setResourceDescriptions(new ResourceDescription[0]);
136: }
137:
138: /**
139: * Compute the status for creating resources from the descriptions. A status
140: * severity of <code>OK</code> indicates that the create is likely to be
141: * successful. A status severity of <code>ERROR</code> indicates that the
142: * operation is no longer valid. Other status severities are open to
143: * interpretation by the caller.
144: *
145: * Note this method may be called on initial creation of a resource, or when
146: * a create or delete operation is being undone or redone. Therefore, this
147: * method should check conditions that can change over the life of the
148: * operation, such as the existence of the information needed to carry out
149: * the operation. One-time static checks should typically be done by the
150: * caller (such as the action that creates the operation) so that the user
151: * is not continually prompted or warned about conditions that were
152: * acceptable at the time of original execution.
153: *
154: * @param allowOverwrite
155: * a boolean that specifies whether resource creation should be
156: * allowed to overwrite an existent resource.
157: */
158: protected IStatus computeCreateStatus(boolean allowOverwrite) {
159: if (resourceDescriptions == null
160: || resourceDescriptions.length == 0) {
161: markInvalid();
162: return getErrorStatus(UndoMessages.AbstractResourcesOperation_NotEnoughInfo);
163: }
164: for (int i = 0; i < resourceDescriptions.length; i++) {
165: // Check for enough info to restore the resource
166: if (!resourceDescriptions[i].isValid()) {
167: markInvalid();
168: return getErrorStatus(UndoMessages.AbstractResourcesOperation_InvalidRestoreInfo);
169: } else if (!allowOverwrite
170: && resourceDescriptions[i].verifyExistence(false)) {
171: // overwrites are not allowed and the resource already exists
172: markInvalid();
173: return getErrorStatus(UndoMessages.AbstractResourcesOperation_ResourcesAlreadyExist);
174: }
175: }
176: return Status.OK_STATUS;
177: }
178:
179: /**
180: * Compute the status for deleting resources. A status severity of
181: * <code>OK</code> indicates that the delete is likely to be successful. A
182: * status severity of <code>ERROR</code> indicates that the operation is
183: * no longer valid. Other status severities are open to interpretation by
184: * the caller.
185: *
186: * Note this method may be called on initial deletion of a resource, or when
187: * a create or delete operation is being undone or redone. Therefore, this
188: * method should check conditions that can change over the life of the
189: * operation, such as the existence of the resources to be deleted. One-time
190: * static checks should typically be done by the caller (such as the action
191: * that creates the operation) so that the user is not continually prompted
192: * or warned about conditions that were acceptable at the time of original
193: * execution.
194: */
195: protected IStatus computeDeleteStatus() {
196: if (resources == null || resources.length == 0) {
197: markInvalid();
198: return getErrorStatus(UndoMessages.AbstractResourcesOperation_NotEnoughInfo);
199: }
200: if (!resourcesExist()) {
201: markInvalid();
202: return getErrorStatus(UndoMessages.AbstractResourcesOperation_ResourcesDoNotExist);
203: }
204: return checkReadOnlyResources(resources);
205: }
206:
207: /**
208: * Check the specified resources for read only state, and return a
209: * status indicating whether the resources can be deleted.
210: */
211: IStatus checkReadOnlyResources(IResource[] resourcesToCheck) {
212: // Check read only status if we are permitted
213: // to consult the user.
214: if (!quietCompute) {
215: ReadOnlyStateChecker checker = new ReadOnlyStateChecker(
216: getShell(null),
217: IDEWorkbenchMessages.DeleteResourceAction_title1,
218: IDEWorkbenchMessages.DeleteResourceAction_readOnlyQuestion);
219: checker.setIgnoreLinkedResources(true);
220: IResource[] approvedResources = checker
221: .checkReadOnlyResources(resourcesToCheck);
222: if (approvedResources.length == 0) {
223: // Consider this a cancelled redo.
224: return Status.CANCEL_STATUS;
225: }
226: // Redefine the redo to only include the approved ones.
227: setTargetResources(approvedResources);
228: }
229: return Status.OK_STATUS;
230: }
231:
232: /**
233: * Set the array of resource descriptions describing resources to be
234: * restored when undoing or redoing this operation.
235: *
236: * @param descriptions
237: * the array of resource descriptions
238: */
239: protected void setResourceDescriptions(
240: ResourceDescription[] descriptions) {
241: if (descriptions == null) {
242: resourceDescriptions = new ResourceDescription[0];
243: } else {
244: resourceDescriptions = descriptions;
245: }
246: }
247:
248: /*
249: * (non-Javadoc)
250: *
251: * @see org.eclipse.ui.ide.undo.AbstractWorkspaceOperation#appendDescriptiveText(java.lang.StringBuffer)
252: */
253: protected void appendDescriptiveText(StringBuffer text) {
254: super .appendDescriptiveText(text);
255: text.append(" resourceDescriptions: "); //$NON-NLS-1$
256: text.append(resourceDescriptions);
257: text.append('\'');
258: }
259:
260: /**
261: * Compute a scheduling rule for creating resources.
262: *
263: * @return a scheduling rule appropriate for creating the resources
264: * specified in the resource descriptions
265: */
266: protected ISchedulingRule computeCreateSchedulingRule() {
267: ISchedulingRule[] ruleArray = new ISchedulingRule[resourceDescriptions.length * 3];
268:
269: for (int i = 0; i < resourceDescriptions.length; i++) {
270: IResource resource = resourceDescriptions[i]
271: .createResourceHandle();
272: // Need a rule for creating...
273: ruleArray[i * 3] = getWorkspaceRuleFactory().createRule(
274: resource);
275: // ...and modifying
276: ruleArray[i * 3 + 1] = getWorkspaceRuleFactory()
277: .modifyRule(resource);
278: // ...and changing the charset
279: ruleArray[i * 3 + 2] = getWorkspaceRuleFactory()
280: .charsetRule(resource);
281:
282: }
283: return MultiRule.combine(ruleArray);
284: }
285:
286: /**
287: * Compute a scheduling rule for deleting resources.
288: *
289: * @return a scheduling rule appropriate for deleting the resources
290: * specified in the receiver.
291: */
292: protected ISchedulingRule computeDeleteSchedulingRule() {
293: ISchedulingRule[] ruleArray = new ISchedulingRule[resources.length * 2];
294: for (int i = 0; i < resources.length; i++) {
295: ruleArray[i * 2] = getWorkspaceRuleFactory().deleteRule(
296: resources[i]);
297: // we include a modify rule because we may have to open a project
298: // to record its resources before deleting it.
299: ruleArray[i * 2 + 1] = getWorkspaceRuleFactory()
300: .modifyRule(resources[i]);
301: }
302: return MultiRule.combine(ruleArray);
303:
304: }
305:
306: /*
307: * (non-Javadoc)
308: *
309: * @see org.eclipse.ui.ide.undo.AbstractWorkspaceOperation#setTargetResources(org.eclipse.core.resources.IResource[])
310: */
311: protected void setTargetResources(IResource[] targetResources) {
312: // Remove any descendants if the parent has also
313: // been specified.
314: List subResources = new ArrayList();
315: for (int i = 0; i < targetResources.length; i++) {
316: IResource subResource = targetResources[i];
317: for (int j = 0; j < targetResources.length; j++) {
318: IResource super Resource = targetResources[j];
319: if (isDescendantOf(subResource, super Resource))
320: subResources.add(subResource);
321: }
322: }
323: IResource[] nestedResourcesRemoved = new IResource[targetResources.length
324: - subResources.size()];
325: int j = 0;
326: for (int i = 0; i < targetResources.length; i++) {
327: if (!subResources.contains(targetResources[i])) {
328: nestedResourcesRemoved[j] = targetResources[i];
329: j++;
330: }
331: }
332: super.setTargetResources(nestedResourcesRemoved);
333: }
334: }
|