0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2006 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.ui.actions;
0011:
0012: import java.io.File;
0013: import java.lang.reflect.InvocationTargetException;
0014: import java.net.URI;
0015: import com.ibm.icu.text.MessageFormat;
0016: import java.util.ArrayList;
0017: import java.util.Arrays;
0018: import java.util.List;
0019:
0020: import org.eclipse.core.commands.ExecutionException;
0021: import org.eclipse.core.filesystem.EFS;
0022: import org.eclipse.core.filesystem.IFileInfo;
0023: import org.eclipse.core.filesystem.IFileStore;
0024: import org.eclipse.core.resources.IContainer;
0025: import org.eclipse.core.resources.IFile;
0026: import org.eclipse.core.resources.IFolder;
0027: import org.eclipse.core.resources.IProject;
0028: import org.eclipse.core.resources.IResource;
0029: import org.eclipse.core.resources.IWorkspace;
0030: import org.eclipse.core.resources.IWorkspaceRoot;
0031: import org.eclipse.core.resources.ResourcesPlugin;
0032: import org.eclipse.core.resources.WorkspaceJob;
0033: import org.eclipse.core.runtime.CoreException;
0034: import org.eclipse.core.runtime.IAdaptable;
0035: import org.eclipse.core.runtime.IPath;
0036: import org.eclipse.core.runtime.IProgressMonitor;
0037: import org.eclipse.core.runtime.IStatus;
0038: import org.eclipse.core.runtime.MultiStatus;
0039: import org.eclipse.core.runtime.OperationCanceledException;
0040: import org.eclipse.core.runtime.Path;
0041: import org.eclipse.core.runtime.Status;
0042: import org.eclipse.core.runtime.SubProgressMonitor;
0043: import org.eclipse.jface.dialogs.ErrorDialog;
0044: import org.eclipse.jface.dialogs.IDialogConstants;
0045: import org.eclipse.jface.dialogs.IInputValidator;
0046: import org.eclipse.jface.dialogs.InputDialog;
0047: import org.eclipse.jface.dialogs.MessageDialog;
0048: import org.eclipse.jface.operation.IRunnableWithProgress;
0049: import org.eclipse.jface.window.Window;
0050: import org.eclipse.osgi.util.NLS;
0051: import org.eclipse.swt.SWT;
0052: import org.eclipse.swt.widgets.Display;
0053: import org.eclipse.swt.widgets.Shell;
0054: import org.eclipse.ui.PlatformUI;
0055: import org.eclipse.ui.dialogs.IOverwriteQuery;
0056: import org.eclipse.ui.ide.undo.AbstractWorkspaceOperation;
0057: import org.eclipse.ui.ide.undo.CopyResourcesOperation;
0058: import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
0059: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
0060: import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
0061: import org.eclipse.ui.internal.ide.StatusUtil;
0062: import org.eclipse.ui.internal.ide.dialogs.IDEResourceInfoUtils;
0063: import org.eclipse.ui.wizards.datatransfer.FileStoreStructureProvider;
0064: import org.eclipse.ui.wizards.datatransfer.ImportOperation;
0065:
0066: /**
0067: * Perform the copy of file and folder resources from the clipboard when paste
0068: * action is invoked.
0069: * <p>
0070: * This class may be instantiated; it is not intended to be subclassed.
0071: * </p>
0072: */
0073: public class CopyFilesAndFoldersOperation {
0074:
0075: /**
0076: * Status containing the errors detected when running the operation or
0077: * <code>null</code> if no errors detected.
0078: */
0079: private MultiStatus errorStatus;
0080:
0081: /**
0082: * The parent shell used to show any dialogs.
0083: */
0084: private Shell messageShell;
0085:
0086: /**
0087: * Whether or not the copy has been canceled by the user.
0088: */
0089: private boolean canceled = false;
0090:
0091: /**
0092: * Overwrite all flag.
0093: */
0094: private boolean alwaysOverwrite = false;
0095:
0096: private String[] modelProviderIds;
0097:
0098: /**
0099: * Returns a new name for a copy of the resource at the given path in the
0100: * given workspace. This name is determined automatically.
0101: *
0102: * @param originalName
0103: * the full path of the resource
0104: * @param workspace
0105: * the workspace
0106: * @return the new full path for the copy
0107: */
0108: static IPath getAutoNewNameFor(IPath originalName,
0109: IWorkspace workspace) {
0110: int counter = 1;
0111: String resourceName = originalName.lastSegment();
0112: IPath leadupSegment = originalName.removeLastSegments(1);
0113:
0114: while (true) {
0115: String nameSegment;
0116:
0117: if (counter > 1) {
0118: nameSegment = NLS
0119: .bind(
0120: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyNameTwoArgs,
0121: new Integer(counter), resourceName);
0122: } else {
0123: nameSegment = NLS
0124: .bind(
0125: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyNameOneArg,
0126: resourceName);
0127: }
0128:
0129: IPath pathToTry = leadupSegment.append(nameSegment);
0130:
0131: if (!workspace.getRoot().exists(pathToTry)) {
0132: return pathToTry;
0133: }
0134:
0135: counter++;
0136: }
0137: }
0138:
0139: /**
0140: * Creates a new operation initialized with a shell.
0141: *
0142: * @param shell
0143: * parent shell for error dialogs
0144: */
0145: public CopyFilesAndFoldersOperation(Shell shell) {
0146: messageShell = shell;
0147: }
0148:
0149: /**
0150: * Returns whether this operation is able to perform on-the-fly
0151: * auto-renaming of resources with name collisions.
0152: *
0153: * @return <code>true</code> if auto-rename is supported, and
0154: * <code>false</code> otherwise
0155: */
0156: protected boolean canPerformAutoRename() {
0157: return true;
0158: }
0159:
0160: /**
0161: * Returns the message for querying deep copy/move of a linked resource.
0162: *
0163: * @param source
0164: * resource the query is made for
0165: * @return the deep query message
0166: */
0167: protected String getDeepCheckQuestion(IResource source) {
0168: return NLS
0169: .bind(
0170: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_deepCopyQuestion,
0171: source.getFullPath().makeRelative());
0172: }
0173:
0174: /**
0175: * Checks whether the infos exist.
0176: *
0177: * @param stores
0178: * the file infos to test
0179: * @return Multi status with one error message for each missing file.
0180: */
0181: IStatus checkExist(IFileStore[] stores) {
0182: MultiStatus multiStatus = new MultiStatus(PlatformUI.PLUGIN_ID,
0183: IStatus.OK, getProblemsMessage(), null);
0184:
0185: for (int i = 0; i < stores.length; i++) {
0186: if (stores[i].fetchInfo().exists() == false) {
0187: String message = NLS
0188: .bind(
0189: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceDeleted,
0190: stores[i].getName());
0191: IStatus status = new Status(IStatus.ERROR,
0192: PlatformUI.PLUGIN_ID, IStatus.OK, message, null);
0193: multiStatus.add(status);
0194: }
0195: }
0196: return multiStatus;
0197: }
0198:
0199: /**
0200: * Checks whether the resources with the given names exist.
0201: *
0202: * @param resources
0203: * IResources to checl
0204: * @return Multi status with one error message for each missing file.
0205: */
0206: IStatus checkExist(IResource[] resources) {
0207: MultiStatus multiStatus = new MultiStatus(PlatformUI.PLUGIN_ID,
0208: IStatus.OK, getProblemsMessage(), null);
0209:
0210: for (int i = 0; i < resources.length; i++) {
0211: IResource resource = resources[i];
0212: if (resource != null) {
0213: URI location = resource.getLocationURI();
0214: String message = null;
0215: if (location != null) {
0216: IFileInfo info = IDEResourceInfoUtils
0217: .getFileInfo(location);
0218: if (info == null || info.exists() == false) {
0219: if (resource.isLinked()) {
0220: message = NLS
0221: .bind(
0222: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_missingLinkTarget,
0223: resource.getName());
0224: } else {
0225: message = NLS
0226: .bind(
0227: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceDeleted,
0228: resource.getName());
0229: }
0230: }
0231: }
0232: if (message != null) {
0233: IStatus status = new Status(IStatus.ERROR,
0234: PlatformUI.PLUGIN_ID, IStatus.OK, message,
0235: null);
0236: multiStatus.add(status);
0237: }
0238: }
0239: }
0240: return multiStatus;
0241: }
0242:
0243: /**
0244: * Check if the user wishes to overwrite the supplied resource or all
0245: * resources.
0246: *
0247: * @param source
0248: * the source resource
0249: * @param destination
0250: * the resource to be overwritten
0251: * @return one of IDialogConstants.YES_ID, IDialogConstants.YES_TO_ALL_ID,
0252: * IDialogConstants.NO_ID, IDialogConstants.CANCEL_ID indicating
0253: * whether the current resource or all resources can be overwritten,
0254: * or if the operation should be canceled.
0255: */
0256: private int checkOverwrite(final IResource source,
0257: final IResource destination) {
0258: final int[] result = new int[1];
0259:
0260: // Dialogs need to be created and opened in the UI thread
0261: Runnable query = new Runnable() {
0262: public void run() {
0263: String message;
0264: int resultId[] = { IDialogConstants.YES_ID,
0265: IDialogConstants.YES_TO_ALL_ID,
0266: IDialogConstants.NO_ID,
0267: IDialogConstants.CANCEL_ID };
0268: String labels[] = new String[] {
0269: IDialogConstants.YES_LABEL,
0270: IDialogConstants.YES_TO_ALL_LABEL,
0271: IDialogConstants.NO_LABEL,
0272: IDialogConstants.CANCEL_LABEL };
0273:
0274: if (destination.getType() == IResource.FOLDER) {
0275: if (homogenousResources(source, destination)) {
0276: message = NLS
0277: .bind(
0278: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteMergeQuestion,
0279: destination.getFullPath()
0280: .makeRelative());
0281: } else {
0282: if (destination.isLinked()) {
0283: message = NLS
0284: .bind(
0285: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteNoMergeLinkQuestion,
0286: destination.getFullPath()
0287: .makeRelative());
0288: } else {
0289: message = NLS
0290: .bind(
0291: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteNoMergeNoLinkQuestion,
0292: destination.getFullPath()
0293: .makeRelative());
0294: }
0295: resultId = new int[] { IDialogConstants.YES_ID,
0296: IDialogConstants.NO_ID,
0297: IDialogConstants.CANCEL_ID };
0298: labels = new String[] {
0299: IDialogConstants.YES_LABEL,
0300: IDialogConstants.NO_LABEL,
0301: IDialogConstants.CANCEL_LABEL };
0302: }
0303: } else {
0304: String[] bindings = new String[] {
0305: IDEResourceInfoUtils
0306: .getLocationText(destination),
0307: IDEResourceInfoUtils
0308: .getDateStringValue(destination),
0309: IDEResourceInfoUtils
0310: .getLocationText(source),
0311: IDEResourceInfoUtils
0312: .getDateStringValue(source) };
0313: message = NLS
0314: .bind(
0315: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteWithDetailsQuestion,
0316: bindings);
0317: }
0318: MessageDialog dialog = new MessageDialog(
0319: messageShell,
0320: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceExists,
0321: null, message, MessageDialog.QUESTION, labels,
0322: 0);
0323: dialog.open();
0324: if (dialog.getReturnCode() == SWT.DEFAULT) {
0325: // A window close returns SWT.DEFAULT, which has to be
0326: // mapped to a cancel
0327: result[0] = IDialogConstants.CANCEL_ID;
0328: } else {
0329: result[0] = resultId[dialog.getReturnCode()];
0330: }
0331: }
0332: };
0333: messageShell.getDisplay().syncExec(query);
0334: return result[0];
0335: }
0336:
0337: /**
0338: * Recursively collects existing files in the specified destination path.
0339: *
0340: * @param destinationPath
0341: * destination path to check for existing files
0342: * @param copyResources
0343: * resources that may exist in the destination
0344: * @param existing
0345: * holds the collected existing files
0346: */
0347: private void collectExistingReadonlyFiles(IPath destinationPath,
0348: IResource[] copyResources, ArrayList existing) {
0349: IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace()
0350: .getRoot();
0351:
0352: for (int i = 0; i < copyResources.length; i++) {
0353: IResource source = copyResources[i];
0354: IPath newDestinationPath = destinationPath.append(source
0355: .getName());
0356: IResource newDestination = workspaceRoot
0357: .findMember(newDestinationPath);
0358: IFolder folder;
0359:
0360: if (newDestination == null) {
0361: continue;
0362: }
0363: folder = getFolder(newDestination);
0364: if (folder != null) {
0365: IFolder sourceFolder = getFolder(source);
0366:
0367: if (sourceFolder != null) {
0368: try {
0369: collectExistingReadonlyFiles(
0370: newDestinationPath, sourceFolder
0371: .members(), existing);
0372: } catch (CoreException exception) {
0373: recordError(exception);
0374: }
0375: }
0376: } else {
0377: IFile file = getFile(newDestination);
0378:
0379: if (file != null) {
0380: if (file.isReadOnly()) {
0381: existing.add(file);
0382: }
0383: if (getValidateConflictSource()) {
0384: IFile sourceFile = getFile(source);
0385: if (sourceFile != null) {
0386: existing.add(sourceFile);
0387: }
0388: }
0389: }
0390: }
0391: }
0392: }
0393:
0394: /**
0395: * Copies the resources to the given destination. This method is called
0396: * recursively to merge folders during folder copy.
0397: *
0398: * @param resources
0399: * the resources to copy
0400: * @param destination
0401: * destination to which resources will be copied
0402: * @param subMonitor
0403: * a progress monitor for showing progress and for cancelation
0404: *
0405: * @deprecated As of 3.3, the work is performed in the undoable operation
0406: * created in
0407: * {@link #getUndoableCopyOrMoveOperation(IResource[], IPath)}
0408: */
0409: protected void copy(IResource[] resources, IPath destination,
0410: IProgressMonitor subMonitor) throws CoreException {
0411:
0412: subMonitor
0413: .beginTask(
0414: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_CopyResourcesTask,
0415: resources.length);
0416:
0417: for (int i = 0; i < resources.length; i++) {
0418: IResource source = resources[i];
0419: IPath destinationPath = destination
0420: .append(source.getName());
0421: IWorkspace workspace = source.getWorkspace();
0422: IWorkspaceRoot workspaceRoot = workspace.getRoot();
0423: IResource existing = workspaceRoot
0424: .findMember(destinationPath);
0425: if (source.getType() == IResource.FOLDER
0426: && existing != null) {
0427: // the resource is a folder and it exists in the destination,
0428: // copy the
0429: // children of the folder.
0430: if (homogenousResources(source, existing)) {
0431: IResource[] children = ((IContainer) source)
0432: .members();
0433: copy(children, destinationPath,
0434: new SubProgressMonitor(subMonitor, 1));
0435: } else {
0436: // delete the destination folder, copying a linked folder
0437: // over an unlinked one or vice versa. Fixes bug 28772.
0438: delete(existing, new SubProgressMonitor(subMonitor,
0439: 0));
0440: source.copy(destinationPath, IResource.SHALLOW,
0441: new SubProgressMonitor(subMonitor, 1));
0442: }
0443: } else {
0444: if (existing != null) {
0445: if (homogenousResources(source, existing)) {
0446: copyExisting(source, existing,
0447: new SubProgressMonitor(subMonitor, 1));
0448: } else {
0449: // Copying a linked resource over unlinked or vice
0450: // versa.
0451: // Can't use setContents here. Fixes bug 28772.
0452: delete(existing, new SubProgressMonitor(
0453: subMonitor, 0));
0454: source.copy(destinationPath, IResource.SHALLOW,
0455: new SubProgressMonitor(subMonitor, 1));
0456: }
0457: } else {
0458: source.copy(destinationPath, IResource.SHALLOW,
0459: new SubProgressMonitor(subMonitor, 1));
0460:
0461: }
0462:
0463: if (subMonitor.isCanceled()) {
0464: throw new OperationCanceledException();
0465: }
0466: }
0467: }
0468: }
0469:
0470: /**
0471: * Sets the content of the existing file to the source file content.
0472: *
0473: * @param source
0474: * source file to copy
0475: * @param existing
0476: * existing file to set the source content in
0477: * @param subMonitor
0478: * a progress monitor for showing progress and for cancelation
0479: * @throws CoreException
0480: * setContents failed
0481: */
0482: private void copyExisting(IResource source, IResource existing,
0483: IProgressMonitor subMonitor) throws CoreException {
0484: IFile existingFile = getFile(existing);
0485:
0486: if (existingFile != null) {
0487: IFile sourceFile = getFile(source);
0488:
0489: if (sourceFile != null) {
0490: existingFile.setContents(sourceFile.getContents(),
0491: IResource.KEEP_HISTORY, new SubProgressMonitor(
0492: subMonitor, 0));
0493: }
0494: }
0495: }
0496:
0497: /**
0498: * Copies the given resources to the destination. The current Thread is
0499: * halted while the resources are copied using a WorkspaceModifyOperation.
0500: * This method should be called from the UIThread.
0501: *
0502: * @param resources
0503: * the resources to copy
0504: * @param destination
0505: * destination to which resources will be copied
0506: * @return IResource[] the resulting {@link IResource}[]
0507: * @see WorkspaceModifyOperation
0508: * @see Display#getThread()
0509: * @see Thread#currentThread()
0510: */
0511: public IResource[] copyResources(final IResource[] resources,
0512: IContainer destination) {
0513: return copyResources(resources, destination, true, null);
0514: }
0515:
0516: /**
0517: * Copies the given resources to the destination in the current Thread
0518: * without forking a new Thread or blocking using a
0519: * WorkspaceModifyOperation. It recommended that this method only be called
0520: * from a {@link WorkspaceJob} to avoid possible deadlock.
0521: *
0522: * @param resources
0523: * the resources to copy
0524: * @param destination
0525: * destination to which resources will be copied
0526: * @param monitor
0527: * the monitor that information will be sent to.
0528: * @return IResource[] the resulting {@link IResource}[]
0529: * @see WorkspaceModifyOperation
0530: * @see WorkspaceJob
0531: * @since 3.2
0532: */
0533: public IResource[] copyResourcesInCurrentThread(
0534: final IResource[] resources, IContainer destination,
0535: IProgressMonitor monitor) {
0536: return copyResources(resources, destination, false, monitor);
0537: }
0538:
0539: /**
0540: * Copies the given resources to the destination.
0541: *
0542: * @param resources
0543: * the resources to copy
0544: * @param destination
0545: * destination to which resources will be copied
0546: * @return IResource[] the resulting {@link IResource}[]
0547: */
0548: private IResource[] copyResources(final IResource[] resources,
0549: IContainer destination, boolean fork,
0550: IProgressMonitor monitor) {
0551: final IPath destinationPath = destination.getFullPath();
0552: final IResource[][] copiedResources = new IResource[1][0];
0553:
0554: // test resources for existence separate from validate API.
0555: // Validate is performance critical and resource exists
0556: // check is potentially slow. Fixes bugs 16129/28602.
0557: IStatus resourceStatus = checkExist(resources);
0558: if (resourceStatus.getSeverity() != IStatus.OK) {
0559: displayError(resourceStatus);
0560: return copiedResources[0];
0561: }
0562: String errorMsg = validateDestination(destination, resources);
0563: if (errorMsg != null) {
0564: displayError(errorMsg);
0565: return copiedResources[0];
0566: }
0567:
0568: IRunnableWithProgress op = new IRunnableWithProgress() {
0569: public void run(IProgressMonitor monitor) {
0570: copyResources(resources, destinationPath,
0571: copiedResources, monitor);
0572: }
0573: };
0574:
0575: try {
0576: PlatformUI.getWorkbench().getProgressService().run(fork,
0577: true, op);
0578: } catch (InterruptedException e) {
0579: return copiedResources[0];
0580: } catch (InvocationTargetException e) {
0581: display(e);
0582: }
0583:
0584: // If errors occurred, open an Error dialog
0585: if (errorStatus != null) {
0586: displayError(errorStatus);
0587: errorStatus = null;
0588: }
0589:
0590: return copiedResources[0];
0591: }
0592:
0593: /**
0594: * Return whether the operation is a move or a copy
0595: *
0596: * @return whether the operation is a move or a copy
0597: * @since 3.2
0598: */
0599: protected boolean isMove() {
0600: return false;
0601: }
0602:
0603: private void display(InvocationTargetException e) {
0604: // CoreExceptions are collected above, but unexpected runtime
0605: // exceptions and errors may still occur.
0606: IDEWorkbenchPlugin
0607: .getDefault()
0608: .getLog()
0609: .log(
0610: StatusUtil
0611: .newStatus(
0612: IStatus.ERROR,
0613: MessageFormat
0614: .format(
0615: "Exception in {0}.performCopy(): {1}", //$NON-NLS-1$
0616: new Object[] {
0617: getClass()
0618: .getName(),
0619: e
0620: .getTargetException() }),
0621: null));
0622: displayError(NLS
0623: .bind(
0624: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_internalError,
0625: e.getTargetException().getMessage()));
0626: }
0627:
0628: /**
0629: * Copies the given URIS and folders to the destination. The current Thread
0630: * is halted while the resources are copied using a
0631: * WorkspaceModifyOperation. This method should be called from the UI
0632: * Thread.
0633: *
0634: * @param uris
0635: * the URIs to copy
0636: * @param destination
0637: * destination to which files will be copied
0638: * @see WorkspaceModifyOperation
0639: * @see Display#getThread()
0640: * @see Thread#currentThread()
0641: * @since 3.2
0642: */
0643: public void copyFiles(URI[] uris, IContainer destination) {
0644: IFileStore[] stores = buildFileStores(uris);
0645: if (stores == null) {
0646: return;
0647: }
0648:
0649: copyFileStores(destination, stores, true, null);
0650: }
0651:
0652: /**
0653: * Copies the given files and folders to the destination without forking a
0654: * new Thread or blocking using a WorkspaceModifyOperation. It is
0655: * recommended that this method only be called from a {@link WorkspaceJob}
0656: * to avoid possible deadlock.
0657: *
0658: * @param uris
0659: * the URIs to copy
0660: * @param destination
0661: * destination to which URIS will be copied
0662: * @param monitor
0663: * the monitor that information will be sent to.
0664: * @see WorkspaceModifyOperation
0665: * @see WorkspaceJob
0666: * @since 3.2
0667: */
0668: public void copyFilesInCurrentThread(URI[] uris,
0669: IContainer destination, IProgressMonitor monitor) {
0670: IFileStore[] stores = buildFileStores(uris);
0671: if (stores == null) {
0672: return;
0673: }
0674:
0675: copyFileStores(destination, stores, false, monitor);
0676: }
0677:
0678: /**
0679: * Build the collection of fileStores that map to fileNames. If any of them
0680: * cannot be found then match then return <code>null</code>.
0681: *
0682: * @param uris
0683: * @return IFileStore[]
0684: */
0685: private IFileStore[] buildFileStores(URI[] uris) {
0686: IFileStore[] stores = new IFileStore[uris.length];
0687: for (int i = 0; i < uris.length; i++) {
0688: IFileStore store;
0689: try {
0690: store = EFS.getStore(uris[i]);
0691: } catch (CoreException e) {
0692: IDEWorkbenchPlugin.log(e.getMessage(), e);
0693: reportFileInfoNotFound(uris[i].toString());
0694: return null;
0695: }
0696: if (store == null) {
0697: reportFileInfoNotFound(uris[i].toString());
0698: return null;
0699: }
0700: stores[i] = store;
0701: }
0702: return stores;
0703:
0704: }
0705:
0706: /**
0707: * Copies the given files and folders to the destination. The current Thread
0708: * is halted while the resources are copied using a
0709: * WorkspaceModifyOperation. This method should be called from the UI
0710: * Thread.
0711: *
0712: * @param fileNames
0713: * names of the files to copy
0714: * @param destination
0715: * destination to which files will be copied
0716: * @see WorkspaceModifyOperation
0717: * @see Display#getThread()
0718: * @see Thread#currentThread()
0719: * @since 3.2
0720: */
0721: public void copyFiles(final String[] fileNames,
0722: IContainer destination) {
0723: IFileStore[] stores = buildFileStores(fileNames);
0724: if (stores == null) {
0725: return;
0726: }
0727:
0728: copyFileStores(destination, stores, true, null);
0729: }
0730:
0731: /**
0732: * Copies the given files and folders to the destination without forking a
0733: * new Thread or blocking using a WorkspaceModifyOperation. It is
0734: * recommended that this method only be called from a {@link WorkspaceJob}
0735: * to avoid possible deadlock.
0736: *
0737: * @param fileNames
0738: * names of the files to copy
0739: * @param destination
0740: * destination to which files will be copied
0741: * @param monitor
0742: * the monitor that information will be sent to.
0743: * @see WorkspaceModifyOperation
0744: * @see WorkspaceJob
0745: * @since 3.2
0746: */
0747: public void copyFilesInCurrentThread(final String[] fileNames,
0748: IContainer destination, IProgressMonitor monitor) {
0749: IFileStore[] stores = buildFileStores(fileNames);
0750: if (stores == null) {
0751: return;
0752: }
0753:
0754: copyFileStores(destination, stores, false, monitor);
0755: }
0756:
0757: /**
0758: * Build the collection of fileStores that map to fileNames. If any of them
0759: * cannot be found then match then return null.
0760: *
0761: * @param fileNames
0762: * @return IFileStore[]
0763: */
0764: private IFileStore[] buildFileStores(final String[] fileNames) {
0765: IFileStore[] stores = new IFileStore[fileNames.length];
0766: for (int i = 0; i < fileNames.length; i++) {
0767: IFileStore store = IDEResourceInfoUtils
0768: .getFileStore(fileNames[i]);
0769: if (store == null) {
0770: reportFileInfoNotFound(fileNames[i]);
0771: return null;
0772: }
0773: stores[i] = store;
0774: }
0775: return stores;
0776: }
0777:
0778: /**
0779: * Report that a file info could not be found.
0780: *
0781: * @param fileName
0782: */
0783: private void reportFileInfoNotFound(final String fileName) {
0784:
0785: messageShell.getDisplay().syncExec(new Runnable() {
0786: public void run() {
0787: ErrorDialog
0788: .openError(
0789: messageShell,
0790: getProblemsTitle(),
0791: NLS
0792: .bind(
0793: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_infoNotFound,
0794: fileName), null);
0795: }
0796: });
0797: }
0798:
0799: /**
0800: * Copies the given files and folders to the destination.
0801: *
0802: * @param stores
0803: * the file stores to copy
0804: * @param destination
0805: * destination to which files will be copied
0806: */
0807: private void copyFileStores(IContainer destination,
0808: final IFileStore[] stores, boolean fork,
0809: IProgressMonitor monitor) {
0810: // test files for existence separate from validate API
0811: // because an external file may not exist until the copy actually
0812: // takes place (e.g., WinZip contents).
0813: IStatus fileStatus = checkExist(stores);
0814: if (fileStatus.getSeverity() != IStatus.OK) {
0815: displayError(fileStatus);
0816: return;
0817: }
0818: String errorMsg = validateImportDestinationInternal(
0819: destination, stores);
0820: if (errorMsg != null) {
0821: displayError(errorMsg);
0822: return;
0823: }
0824: final IPath destinationPath = destination.getFullPath();
0825:
0826: if (fork) {
0827: WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
0828: public void execute(IProgressMonitor monitor) {
0829: copyFileStores(stores, destinationPath, monitor);
0830: }
0831: };
0832: try {
0833: PlatformUI.getWorkbench().getProgressService().run(
0834: true, true, op);
0835: } catch (InterruptedException e) {
0836: return;
0837: } catch (InvocationTargetException exception) {
0838: display(exception);
0839: }
0840: } else {
0841: copyFileStores(stores, destinationPath, monitor);
0842: }
0843:
0844: // If errors occurred, open an Error dialog
0845: if (errorStatus != null) {
0846: displayError(errorStatus);
0847: errorStatus = null;
0848: }
0849: }
0850:
0851: /**
0852: * Display the supplied status in an error dialog.
0853: *
0854: * @param status
0855: * The status to display
0856: */
0857: private void displayError(final IStatus status) {
0858: messageShell.getDisplay().syncExec(new Runnable() {
0859: public void run() {
0860: ErrorDialog.openError(messageShell, getProblemsTitle(),
0861: null, status);
0862: }
0863: });
0864: }
0865:
0866: /**
0867: * Creates a file or folder handle for the source resource as if it were to
0868: * be created in the destination container.
0869: *
0870: * @param destination
0871: * destination container
0872: * @param source
0873: * source resource
0874: * @return IResource file or folder handle, depending on the source type.
0875: */
0876: IResource createLinkedResourceHandle(IContainer destination,
0877: IResource source) {
0878: IWorkspace workspace = destination.getWorkspace();
0879: IWorkspaceRoot workspaceRoot = workspace.getRoot();
0880: IPath linkPath = destination.getFullPath().append(
0881: source.getName());
0882: IResource linkHandle;
0883:
0884: if (source.getType() == IResource.FOLDER) {
0885: linkHandle = workspaceRoot.getFolder(linkPath);
0886: } else {
0887: linkHandle = workspaceRoot.getFile(linkPath);
0888: }
0889: return linkHandle;
0890: }
0891:
0892: /**
0893: * Removes the given resource from the workspace.
0894: *
0895: * @param resource
0896: * resource to remove from the workspace
0897: * @param monitor
0898: * a progress monitor for showing progress and for cancelation
0899: * @return true the resource was deleted successfully false the resource was
0900: * not deleted because a CoreException occurred
0901: */
0902: boolean delete(IResource resource, IProgressMonitor monitor) {
0903: boolean force = false; // don't force deletion of out-of-sync resources
0904:
0905: if (resource.getType() == IResource.PROJECT) {
0906: // if it's a project, ask whether content should be deleted too
0907: IProject project = (IProject) resource;
0908: try {
0909: project.delete(true, force, monitor);
0910: } catch (CoreException e) {
0911: recordError(e); // log error
0912: return false;
0913: }
0914: } else {
0915: // if it's not a project, just delete it
0916: int flags = IResource.KEEP_HISTORY;
0917: if (force) {
0918: flags = flags | IResource.FORCE;
0919: }
0920: try {
0921: resource.delete(flags, monitor);
0922: } catch (CoreException e) {
0923: recordError(e); // log error
0924: return false;
0925: }
0926: }
0927: return true;
0928: }
0929:
0930: /**
0931: * Opens an error dialog to display the given message.
0932: *
0933: * @param message
0934: * the error message to show
0935: */
0936: private void displayError(final String message) {
0937: messageShell.getDisplay().syncExec(new Runnable() {
0938: public void run() {
0939: MessageDialog.openError(messageShell,
0940: getProblemsTitle(), message);
0941: }
0942: });
0943: }
0944:
0945: /**
0946: * Returns the resource either casted to or adapted to an IFile.
0947: *
0948: * @param resource
0949: * resource to cast/adapt
0950: * @return the resource either casted to or adapted to an IFile.
0951: * <code>null</code> if the resource does not adapt to IFile
0952: */
0953: protected IFile getFile(IResource resource) {
0954: if (resource instanceof IFile) {
0955: return (IFile) resource;
0956: }
0957: return (IFile) ((IAdaptable) resource).getAdapter(IFile.class);
0958: }
0959:
0960: /**
0961: * Returns java.io.File objects for the given file names.
0962: *
0963: * @param fileNames
0964: * files to return File object for.
0965: * @return java.io.File objects for the given file names.
0966: * @deprecated As of 3.3, this method is no longer in use anywhere in this
0967: * class and is only provided for backwards compatability with
0968: * subclasses of the receiver.
0969: */
0970: protected File[] getFiles(String[] fileNames) {
0971: File[] files = new File[fileNames.length];
0972:
0973: for (int i = 0; i < fileNames.length; i++) {
0974: files[i] = new File(fileNames[i]);
0975: }
0976: return files;
0977: }
0978:
0979: /**
0980: * Returns the resource either casted to or adapted to an IFolder.
0981: *
0982: * @param resource
0983: * resource to cast/adapt
0984: * @return the resource either casted to or adapted to an IFolder.
0985: * <code>null</code> if the resource does not adapt to IFolder
0986: */
0987: protected IFolder getFolder(IResource resource) {
0988: if (resource instanceof IFolder) {
0989: return (IFolder) resource;
0990: }
0991: return (IFolder) ((IAdaptable) resource)
0992: .getAdapter(IFolder.class);
0993: }
0994:
0995: /**
0996: * Returns a new name for a copy of the resource at the given path in the
0997: * given workspace.
0998: *
0999: * @param originalName
1000: * the full path of the resource
1001: * @param workspace
1002: * the workspace
1003: * @return the new full path for the copy, or <code>null</code> if the
1004: * resource should not be copied
1005: */
1006: private IPath getNewNameFor(final IPath originalName,
1007: final IWorkspace workspace) {
1008: final IResource resource = workspace.getRoot().findMember(
1009: originalName);
1010: final IPath prefix = resource.getFullPath().removeLastSegments(
1011: 1);
1012: final String returnValue[] = { "" }; //$NON-NLS-1$
1013:
1014: messageShell.getDisplay().syncExec(new Runnable() {
1015: public void run() {
1016: IInputValidator validator = new IInputValidator() {
1017: public String isValid(String string) {
1018: if (resource.getName().equals(string)) {
1019: return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_nameMustBeDifferent;
1020: }
1021: IStatus status = workspace.validateName(string,
1022: resource.getType());
1023: if (!status.isOK()) {
1024: return status.getMessage();
1025: }
1026: if (workspace.getRoot().exists(
1027: prefix.append(string))) {
1028: return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_nameExists;
1029: }
1030: return null;
1031: }
1032: };
1033:
1034: InputDialog dialog = new InputDialog(
1035: messageShell,
1036: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_inputDialogTitle,
1037: NLS
1038: .bind(
1039: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_inputDialogMessage,
1040: resource.getName()),
1041: getAutoNewNameFor(originalName, workspace)
1042: .lastSegment().toString(), validator);
1043: dialog.setBlockOnOpen(true);
1044: dialog.open();
1045: if (dialog.getReturnCode() == Window.CANCEL) {
1046: returnValue[0] = null;
1047: } else {
1048: returnValue[0] = dialog.getValue();
1049: }
1050: }
1051: });
1052: if (returnValue[0] == null) {
1053: throw new OperationCanceledException();
1054: }
1055: return prefix.append(returnValue[0]);
1056: }
1057:
1058: /**
1059: * Returns the task title for this operation's progress dialog.
1060: *
1061: * @return the task title
1062: */
1063: protected String getOperationTitle() {
1064: return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_operationTitle;
1065: }
1066:
1067: /**
1068: * Returns the message for this operation's problems dialog.
1069: *
1070: * @return the problems message
1071: */
1072: protected String getProblemsMessage() {
1073: return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_problemMessage;
1074: }
1075:
1076: /**
1077: * Returns the title for this operation's problems dialog.
1078: *
1079: * @return the problems dialog title
1080: */
1081: protected String getProblemsTitle() {
1082: return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyFailedTitle;
1083: }
1084:
1085: /**
1086: * Returns whether the source file in a destination collision will be
1087: * validateEdited together with the collision itself. Returns false. Should
1088: * return true if the source file is to be deleted after the operation.
1089: *
1090: * @return boolean <code>true</code> if the source file in a destination
1091: * collision should be validateEdited. <code>false</code> if only
1092: * the destination should be validated.
1093: */
1094: protected boolean getValidateConflictSource() {
1095: return false;
1096: }
1097:
1098: /**
1099: * Returns whether the given resources are either both linked or both
1100: * unlinked.
1101: *
1102: * @param source
1103: * source resource
1104: * @param destination
1105: * destination resource
1106: * @return boolean <code>true</code> if both resources are either linked
1107: * or unlinked. <code>false</code> otherwise.
1108: */
1109: protected boolean homogenousResources(IResource source,
1110: IResource destination) {
1111: boolean isSourceLinked = source.isLinked();
1112: boolean isDestinationLinked = destination.isLinked();
1113:
1114: return (isSourceLinked && isDestinationLinked || isSourceLinked == false
1115: && isDestinationLinked == false);
1116: }
1117:
1118: /**
1119: * Returns whether the given resource is accessible. Files and folders are
1120: * always considered accessible and a project is accessible if it is open.
1121: *
1122: * @param resource
1123: * the resource
1124: * @return <code>true</code> if the resource is accessible, and
1125: * <code>false</code> if it is not
1126: */
1127: private boolean isAccessible(IResource resource) {
1128: switch (resource.getType()) {
1129: case IResource.FILE:
1130: return true;
1131: case IResource.FOLDER:
1132: return true;
1133: case IResource.PROJECT:
1134: return ((IProject) resource).isOpen();
1135: default:
1136: return false;
1137: }
1138: }
1139:
1140: /**
1141: * Returns whether any of the given source resources are being recopied to
1142: * their current container.
1143: *
1144: * @param sourceResources
1145: * the source resources
1146: * @param destination
1147: * the destination container
1148: * @return <code>true</code> if at least one of the given source
1149: * resource's parent container is the same as the destination
1150: */
1151: boolean isDestinationSameAsSource(IResource[] sourceResources,
1152: IContainer destination) {
1153: IPath destinationLocation = destination.getLocation();
1154:
1155: for (int i = 0; i < sourceResources.length; i++) {
1156: IResource sourceResource = sourceResources[i];
1157: if (sourceResource.getParent().equals(destination)) {
1158: return true;
1159: } else if (destinationLocation != null) {
1160: // do thorough check to catch linked resources. Fixes bug 29913.
1161: IPath sourceLocation = sourceResource.getLocation();
1162: IPath destinationResource = destinationLocation
1163: .append(sourceResource.getName());
1164: if (sourceLocation != null
1165: && sourceLocation
1166: .isPrefixOf(destinationResource)) {
1167: return true;
1168: }
1169: }
1170: }
1171: return false;
1172: }
1173:
1174: /**
1175: * Copies the given resources to the destination container with the given
1176: * name.
1177: * <p>
1178: * Note: the destination container may need to be created prior to copying
1179: * the resources.
1180: * </p>
1181: *
1182: * @param resources
1183: * the resources to copy
1184: * @param destination
1185: * the path of the destination container
1186: * @param monitor
1187: * a progress monitor for showing progress and for cancelation
1188: * @return <code>true</code> if the copy operation completed without
1189: * errors
1190: */
1191: private boolean performCopy(IResource[] resources,
1192: IPath destination, IProgressMonitor monitor) {
1193: try {
1194: AbstractWorkspaceOperation op = getUndoableCopyOrMoveOperation(
1195: resources, destination);
1196: op.setModelProviderIds(getModelProviderIds());
1197: PlatformUI.getWorkbench().getOperationSupport()
1198: .getOperationHistory().execute(
1199: op,
1200: monitor,
1201: WorkspaceUndoUtil
1202: .getUIInfoAdapter(messageShell));
1203: } catch (ExecutionException e) {
1204: if (e.getCause() instanceof CoreException) {
1205: recordError((CoreException) e.getCause());
1206: } else {
1207: IDEWorkbenchPlugin.log(e.getMessage(), e);
1208: displayError(e.getMessage());
1209: }
1210: return false;
1211: }
1212: return true;
1213: }
1214:
1215: /**
1216: * Individually copies the given resources to the specified destination
1217: * container checking for name collisions. If a collision is detected, it is
1218: * saved with a new name.
1219: * <p>
1220: * Note: the destination container may need to be created prior to copying
1221: * the resources.
1222: * </p>
1223: *
1224: * @param resources
1225: * the resources to copy
1226: * @param destination
1227: * the path of the destination container
1228: * @return <code>true</code> if the copy operation completed without
1229: * errors.
1230: */
1231: private boolean performCopyWithAutoRename(IResource[] resources,
1232: IPath destination, IProgressMonitor monitor) {
1233: IWorkspace workspace = resources[0].getWorkspace();
1234: IPath[] destinationPaths = new IPath[resources.length];
1235: try {
1236: for (int i = 0; i < resources.length; i++) {
1237: IResource source = resources[i];
1238: destinationPaths[i] = destination.append(source
1239: .getName());
1240:
1241: if (workspace.getRoot().exists(destinationPaths[i])) {
1242: destinationPaths[i] = getNewNameFor(
1243: destinationPaths[i], workspace);
1244: }
1245: }
1246: CopyResourcesOperation op = new CopyResourcesOperation(
1247: resources,
1248: destinationPaths,
1249: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyTitle);
1250: op.setModelProviderIds(getModelProviderIds());
1251: PlatformUI.getWorkbench().getOperationSupport()
1252: .getOperationHistory().execute(
1253: op,
1254: monitor,
1255: WorkspaceUndoUtil
1256: .getUIInfoAdapter(messageShell));
1257: } catch (ExecutionException e) {
1258: if (e.getCause() instanceof CoreException) {
1259: recordError((CoreException) e.getCause());
1260: } else {
1261: IDEWorkbenchPlugin.log(e.getMessage(), e);
1262: displayError(e.getMessage());
1263: }
1264: return false;
1265: }
1266: return true;
1267: }
1268:
1269: /**
1270: * Performs an import of the given stores into the provided container.
1271: * Returns a status indicating if the import was successful.
1272: *
1273: * @param stores
1274: * stores that are to be imported
1275: * @param target
1276: * container to which the import will be done
1277: * @param monitor
1278: * a progress monitor for showing progress and for cancelation
1279: */
1280: private void performFileImport(IFileStore[] stores,
1281: IContainer target, IProgressMonitor monitor) {
1282: IOverwriteQuery query = new IOverwriteQuery() {
1283: public String queryOverwrite(String pathString) {
1284: if (alwaysOverwrite) {
1285: return ALL;
1286: }
1287:
1288: final String returnCode[] = { CANCEL };
1289: final String msg = NLS
1290: .bind(
1291: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteQuestion,
1292: pathString);
1293: final String[] options = { IDialogConstants.YES_LABEL,
1294: IDialogConstants.YES_TO_ALL_LABEL,
1295: IDialogConstants.NO_LABEL,
1296: IDialogConstants.CANCEL_LABEL };
1297: messageShell.getDisplay().syncExec(new Runnable() {
1298: public void run() {
1299: MessageDialog dialog = new MessageDialog(
1300: messageShell,
1301: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_question,
1302: null, msg, MessageDialog.QUESTION,
1303: options, 0);
1304: dialog.open();
1305: int returnVal = dialog.getReturnCode();
1306: String[] returnCodes = { YES, ALL, NO, CANCEL };
1307: returnCode[0] = returnVal == -1 ? CANCEL
1308: : returnCodes[returnVal];
1309: }
1310: });
1311: if (returnCode[0] == ALL) {
1312: alwaysOverwrite = true;
1313: } else if (returnCode[0] == CANCEL) {
1314: canceled = true;
1315: }
1316: return returnCode[0];
1317: }
1318: };
1319:
1320: ImportOperation op = new ImportOperation(target.getFullPath(),
1321: stores[0].getParent(),
1322: FileStoreStructureProvider.INSTANCE, query, Arrays
1323: .asList(stores));
1324: op.setContext(messageShell);
1325: op.setCreateContainerStructure(false);
1326: try {
1327: op.run(monitor);
1328: } catch (InterruptedException e) {
1329: return;
1330: } catch (InvocationTargetException e) {
1331: if (e.getTargetException() instanceof CoreException) {
1332: displayError(((CoreException) e.getTargetException())
1333: .getStatus());
1334: } else {
1335: display(e);
1336: }
1337: return;
1338: }
1339: // Special case since ImportOperation doesn't throw a CoreException on
1340: // failure.
1341: IStatus status = op.getStatus();
1342: if (!status.isOK()) {
1343: if (errorStatus == null) {
1344: errorStatus = new MultiStatus(PlatformUI.PLUGIN_ID,
1345: IStatus.ERROR, getProblemsMessage(), null);
1346: }
1347: errorStatus.merge(status);
1348: }
1349: }
1350:
1351: /**
1352: * Records the core exception to be displayed to the user once the action is
1353: * finished.
1354: *
1355: * @param error
1356: * a <code>CoreException</code>
1357: */
1358: private void recordError(CoreException error) {
1359: if (errorStatus == null) {
1360: errorStatus = new MultiStatus(PlatformUI.PLUGIN_ID,
1361: IStatus.ERROR, getProblemsMessage(), error);
1362: }
1363:
1364: errorStatus.merge(error.getStatus());
1365: }
1366:
1367: /**
1368: * Checks whether the destination is valid for copying the source resources.
1369: * <p>
1370: * Note this method is for internal use only. It is not API.
1371: * </p>
1372: *
1373: * @param destination
1374: * the destination container
1375: * @param sourceResources
1376: * the source resources
1377: * @return an error message, or <code>null</code> if the path is valid
1378: */
1379: public String validateDestination(IContainer destination,
1380: IResource[] sourceResources) {
1381: if (!isAccessible(destination)) {
1382: return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationAccessError;
1383: }
1384: IContainer firstParent = null;
1385: URI destinationLocation = destination.getLocationURI();
1386: for (int i = 0; i < sourceResources.length; i++) {
1387: IResource sourceResource = sourceResources[i];
1388: if (firstParent == null) {
1389: firstParent = sourceResource.getParent();
1390: } else if (firstParent.equals(sourceResource.getParent()) == false) {
1391: // Resources must have common parent. Fixes bug 33398.
1392: return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_parentNotEqual;
1393: }
1394:
1395: URI sourceLocation = sourceResource.getLocationURI();
1396: if (sourceLocation == null) {
1397: if (sourceResource.isLinked()) {
1398: // Don't allow copying linked resources with undefined path
1399: // variables. See bug 28754.
1400: return NLS
1401: .bind(
1402: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_missingPathVariable,
1403: sourceResource.getName());
1404: }
1405: return NLS
1406: .bind(
1407: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceDeleted,
1408: sourceResource.getName());
1409:
1410: }
1411: if (sourceLocation.equals(destinationLocation)) {
1412: return NLS
1413: .bind(
1414: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_sameSourceAndDest,
1415: sourceResource.getName());
1416: }
1417: // is the source a parent of the destination?
1418: if (new Path(sourceLocation.toString())
1419: .isPrefixOf(new Path(destinationLocation.toString()))) {
1420: return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationDescendentError;
1421: }
1422:
1423: String linkedResourceMessage = validateLinkedResource(
1424: destination, sourceResource);
1425: if (linkedResourceMessage != null) {
1426: return linkedResourceMessage;
1427: }
1428: }
1429: return null;
1430: }
1431:
1432: /**
1433: * Validates that the given source resources can be copied to the
1434: * destination as decided by the VCM provider.
1435: *
1436: * @param destination
1437: * copy destination
1438: * @param sourceResources
1439: * source resources
1440: * @return <code>true</code> all files passed validation or there were no
1441: * files to validate. <code>false</code> one or more files did not
1442: * pass validation.
1443: */
1444: private boolean validateEdit(IContainer destination,
1445: IResource[] sourceResources) {
1446: ArrayList copyFiles = new ArrayList();
1447:
1448: collectExistingReadonlyFiles(destination.getFullPath(),
1449: sourceResources, copyFiles);
1450: if (copyFiles.size() > 0) {
1451: IFile[] files = (IFile[]) copyFiles
1452: .toArray(new IFile[copyFiles.size()]);
1453: IWorkspace workspace = ResourcesPlugin.getWorkspace();
1454: IStatus status = workspace
1455: .validateEdit(files, messageShell);
1456:
1457: canceled = status.isOK() == false;
1458: return status.isOK();
1459: }
1460: return true;
1461: }
1462:
1463: /**
1464: * Checks whether the destination is valid for copying the source files.
1465: * <p>
1466: * Note this method is for internal use only. It is not API.
1467: * </p>
1468: *
1469: * @param destination
1470: * the destination container
1471: * @param sourceNames
1472: * the source file names
1473: * @return an error message, or <code>null</code> if the path is valid
1474: */
1475: public String validateImportDestination(IContainer destination,
1476: String[] sourceNames) {
1477:
1478: IFileStore[] stores = new IFileStore[sourceNames.length];
1479: for (int i = 0; i < sourceNames.length; i++) {
1480: IFileStore store = IDEResourceInfoUtils
1481: .getFileStore(sourceNames[i]);
1482: if (store == null) {
1483: return NLS
1484: .bind(
1485: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_infoNotFound,
1486: sourceNames[i]);
1487: }
1488: stores[i] = store;
1489: }
1490: return validateImportDestinationInternal(destination, stores);
1491:
1492: }
1493:
1494: /**
1495: * Checks whether the destination is valid for copying the source file
1496: * stores.
1497: * <p>
1498: * Note this method is for internal use only. It is not API.
1499: * </p>
1500: * <p>
1501: * TODO Bug 117804. This method has been renamed to avoid a bug in the
1502: * Eclipse compiler with regards to visibility and type resolution when
1503: * linking.
1504: * </p>
1505: *
1506: * @param destination
1507: * the destination container
1508: * @param sourceStores
1509: * the source IFileStore
1510: * @return an error message, or <code>null</code> if the path is valid
1511: */
1512: private String validateImportDestinationInternal(
1513: IContainer destination, IFileStore[] sourceStores) {
1514: if (!isAccessible(destination))
1515: return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationAccessError;
1516:
1517: IFileStore destinationStore;
1518: try {
1519: destinationStore = EFS.getStore(destination
1520: .getLocationURI());
1521: } catch (CoreException exception) {
1522: IDEWorkbenchPlugin.log(exception.getLocalizedMessage(),
1523: exception);
1524: return NLS
1525: .bind(
1526: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_internalError,
1527: exception.getLocalizedMessage());
1528: }
1529: for (int i = 0; i < sourceStores.length; i++) {
1530: IFileStore sourceStore = sourceStores[i];
1531: IFileStore sourceParentStore = sourceStore.getParent();
1532:
1533: if (sourceStore != null) {
1534: if (destinationStore.equals(sourceStore)
1535: || (sourceParentStore != null && destinationStore
1536: .equals(sourceParentStore))) {
1537: return NLS
1538: .bind(
1539: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_importSameSourceAndDest,
1540: sourceStore.getName());
1541: }
1542: // work around bug 16202. replacement for
1543: // sourcePath.isPrefixOf(destinationPath)
1544: IFileStore destinationParent = destinationStore
1545: .getParent();
1546: if (sourceStore.isParentOf(destinationParent)) {
1547: return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationDescendentError;
1548: }
1549:
1550: }
1551: }
1552: return null;
1553: }
1554:
1555: /**
1556: * Check if the destination is valid for the given source resource.
1557: *
1558: * @param destination
1559: * destination container of the operation
1560: * @param source
1561: * source resource
1562: * @return String error message or null if the destination is valid
1563: */
1564: private String validateLinkedResource(IContainer destination,
1565: IResource source) {
1566: if (source.isLinked() == false) {
1567: return null;
1568: }
1569: IWorkspace workspace = destination.getWorkspace();
1570: IResource linkHandle = createLinkedResourceHandle(destination,
1571: source);
1572: IStatus locationStatus = workspace.validateLinkLocation(
1573: linkHandle, source.getRawLocation());
1574:
1575: if (locationStatus.getSeverity() == IStatus.ERROR) {
1576: return locationStatus.getMessage();
1577: }
1578: IPath sourceLocation = source.getLocation();
1579: if (source.getProject().equals(destination.getProject()) == false
1580: && source.getType() == IResource.FOLDER
1581: && sourceLocation != null) {
1582: // prevent merging linked folders that point to the same
1583: // file system folder
1584: try {
1585: IResource[] members = destination.members();
1586: for (int j = 0; j < members.length; j++) {
1587: if (sourceLocation.equals(members[j].getLocation())
1588: && source.getName().equals(
1589: members[j].getName())) {
1590: return NLS
1591: .bind(
1592: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_sameSourceAndDest,
1593: source.getName());
1594: }
1595: }
1596: } catch (CoreException exception) {
1597: displayError(NLS
1598: .bind(
1599: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_internalError,
1600: exception.getMessage()));
1601: }
1602: }
1603: return null;
1604: }
1605:
1606: /**
1607: * Returns whether moving all of the given source resources to the given
1608: * destination container could be done without causing name collisions.
1609: *
1610: * @param destination
1611: * the destination container
1612: * @param sourceResources
1613: * the list of resources
1614: * @return <code>true</code> if there would be no name collisions, and
1615: * <code>false</code> if there would
1616: */
1617: private IResource[] validateNoNameCollisions(
1618: IContainer destination, IResource[] sourceResources) {
1619: List copyItems = new ArrayList();
1620: IWorkspaceRoot workspaceRoot = destination.getWorkspace()
1621: .getRoot();
1622: int overwrite = IDialogConstants.NO_ID;
1623:
1624: // Check to see if we would be overwriting a parent folder.
1625: // Cancel entire copy operation if we do.
1626: for (int i = 0; i < sourceResources.length; i++) {
1627: final IResource sourceResource = sourceResources[i];
1628: final IPath destinationPath = destination.getFullPath()
1629: .append(sourceResource.getName());
1630: final IPath sourcePath = sourceResource.getFullPath();
1631:
1632: IResource newResource = workspaceRoot
1633: .findMember(destinationPath);
1634: if (newResource != null
1635: && destinationPath.isPrefixOf(sourcePath)) {
1636: displayError(NLS
1637: .bind(
1638: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteProblem,
1639: destinationPath, sourcePath));
1640:
1641: canceled = true;
1642: return null;
1643: }
1644: }
1645: // Check for overwrite conflicts
1646: for (int i = 0; i < sourceResources.length; i++) {
1647: final IResource source = sourceResources[i];
1648: final IPath destinationPath = destination.getFullPath()
1649: .append(source.getName());
1650:
1651: IResource newResource = workspaceRoot
1652: .findMember(destinationPath);
1653: if (newResource != null) {
1654: if (overwrite != IDialogConstants.YES_TO_ALL_ID
1655: || (newResource.getType() == IResource.FOLDER && homogenousResources(
1656: source, destination) == false)) {
1657: overwrite = checkOverwrite(source, newResource);
1658: }
1659: if (overwrite == IDialogConstants.YES_ID
1660: || overwrite == IDialogConstants.YES_TO_ALL_ID) {
1661: copyItems.add(source);
1662: } else if (overwrite == IDialogConstants.CANCEL_ID) {
1663: canceled = true;
1664: return null;
1665: }
1666: } else {
1667: copyItems.add(source);
1668: }
1669: }
1670: return (IResource[]) copyItems.toArray(new IResource[copyItems
1671: .size()]);
1672: }
1673:
1674: private void copyResources(final IResource[] resources,
1675: final IPath destinationPath,
1676: final IResource[][] copiedResources,
1677: IProgressMonitor monitor) {
1678: IResource[] copyResources = resources;
1679:
1680: // Fix for bug 31116. Do not provide a task name when
1681: // creating the task.
1682: monitor.beginTask("", 100); //$NON-NLS-1$
1683: monitor.setTaskName(getOperationTitle());
1684: monitor.worked(10); // show some initial progress
1685:
1686: // Checks only required if this is an exisiting container path.
1687: boolean copyWithAutoRename = false;
1688: IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
1689: if (root.exists(destinationPath)) {
1690: IContainer container = (IContainer) root
1691: .findMember(destinationPath);
1692: // If we're copying to the source container then perform
1693: // auto-renames on all resources to avoid name collisions.
1694: if (isDestinationSameAsSource(copyResources, container)
1695: && canPerformAutoRename()) {
1696: copyWithAutoRename = true;
1697: } else {
1698: // If no auto-renaming will be happening, check for
1699: // potential name collisions at the target resource
1700: copyResources = validateNoNameCollisions(container,
1701: copyResources);
1702: if (copyResources == null) {
1703: if (canceled) {
1704: return;
1705: }
1706: displayError(IDEWorkbenchMessages.CopyFilesAndFoldersOperation_nameCollision);
1707: return;
1708: }
1709: if (validateEdit(container, copyResources) == false) {
1710: return;
1711: }
1712: }
1713: }
1714:
1715: errorStatus = null;
1716: if (copyResources.length > 0) {
1717: if (copyWithAutoRename) {
1718: performCopyWithAutoRename(copyResources,
1719: destinationPath, new SubProgressMonitor(
1720: monitor, 90));
1721: } else {
1722: performCopy(copyResources, destinationPath,
1723: new SubProgressMonitor(monitor, 90));
1724: }
1725: }
1726: monitor.done();
1727: copiedResources[0] = copyResources;
1728: }
1729:
1730: private void copyFileStores(final IFileStore[] stores,
1731: final IPath destinationPath, IProgressMonitor monitor) {
1732: // Checks only required if this is an exisiting container path.
1733: IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
1734: if (root.exists(destinationPath)) {
1735: IContainer container = (IContainer) root
1736: .findMember(destinationPath);
1737:
1738: performFileImport(stores, container, monitor);
1739: }
1740: }
1741:
1742: /**
1743: * Returns the model provider ids that are known to the client that
1744: * instantiated this operation.
1745: *
1746: * @return the model provider ids that are known to the client that
1747: * instantiated this operation.
1748: * @since 3.2
1749: */
1750: public String[] getModelProviderIds() {
1751: return modelProviderIds;
1752: }
1753:
1754: /**
1755: * Sets the model provider ids that are known to the client that
1756: * instantiated this operation. Any potential side effects reported by these
1757: * models during validation will be ignored.
1758: *
1759: * @param modelProviderIds
1760: * the model providers known to the client who is using this
1761: * operation.
1762: * @since 3.2
1763: */
1764: public void setModelProviderIds(String[] modelProviderIds) {
1765: this .modelProviderIds = modelProviderIds;
1766: }
1767:
1768: /**
1769: * Returns an AbstractWorkspaceOperation suitable for performing the move or
1770: * copy operation that will move or copy the given resources to the given
1771: * destination path.
1772: *
1773: * @param resources
1774: * the resources to be moved or copied
1775: * @param destinationPath
1776: * the destination path to which the resources should be moved
1777: * @return the operation that should be used to perform the move or cop
1778: * @since 3.3
1779: */
1780: protected AbstractWorkspaceOperation getUndoableCopyOrMoveOperation(
1781: IResource[] resources, IPath destinationPath) {
1782: return new CopyResourcesOperation(
1783: resources,
1784: destinationPath,
1785: IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyTitle);
1786:
1787: }
1788: }
|