| /******************************************************************************* |
| * Copyright (c) 2000, 2016 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Serge Beauchamp (Freescale Semiconductor) - Bug 229633 |
| * Lars Vogel <Lars.Vogel@vogella.com> - Bug 472784 |
| * Patrik Suzzi <psuzzi@gmail.com> - Bug 489250 |
| *******************************************************************************/ |
| package org.eclipse.ui.actions; |
| |
| import java.io.File; |
| import java.lang.reflect.InvocationTargetException; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.filesystem.EFS; |
| import org.eclipse.core.filesystem.IFileInfo; |
| import org.eclipse.core.filesystem.IFileStore; |
| import org.eclipse.core.filesystem.URIUtil; |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.resources.WorkspaceJob; |
| import org.eclipse.core.runtime.Adapters; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.jface.dialogs.ErrorDialog; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.IInputValidator; |
| import org.eclipse.jface.dialogs.InputDialog; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.dialogs.IOverwriteQuery; |
| import org.eclipse.ui.ide.dialogs.ImportTypeDialog; |
| import org.eclipse.ui.ide.undo.AbstractWorkspaceOperation; |
| import org.eclipse.ui.ide.undo.CopyResourcesOperation; |
| import org.eclipse.ui.ide.undo.WorkspaceUndoUtil; |
| import org.eclipse.ui.internal.ide.IDEInternalPreferences; |
| import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; |
| import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; |
| import org.eclipse.ui.internal.ide.StatusUtil; |
| import org.eclipse.ui.internal.ide.dialogs.IDEResourceInfoUtils; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| import org.eclipse.ui.wizards.datatransfer.FileStoreStructureProvider; |
| import org.eclipse.ui.wizards.datatransfer.ImportOperation; |
| |
| import com.ibm.icu.text.MessageFormat; |
| |
| |
| /** |
| * Perform the copy of file and folder resources from the clipboard when paste |
| * action is invoked. |
| * <p> |
| * This class may be instantiated; it is not intended to be subclassed. |
| * </p> |
| * @noextend This class is not intended to be subclassed by clients. |
| */ |
| public class CopyFilesAndFoldersOperation { |
| |
| /** |
| * Status containing the errors detected when running the operation or |
| * <code>null</code> if no errors detected. |
| */ |
| private MultiStatus errorStatus; |
| |
| /** |
| * The parent shell used to show any dialogs. |
| */ |
| private Shell messageShell; |
| |
| /** |
| * Whether or not the copy has been canceled by the user. |
| */ |
| private boolean canceled = false; |
| |
| /** |
| * Whether or not the operation creates virtual folders and links instead of folders |
| * and files. |
| */ |
| private boolean createVirtualFoldersAndLinks = false; |
| |
| /** |
| * Whether or not the operation creates links instead of folders and files. |
| */ |
| private boolean createLinks = false; |
| |
| private String relativeVariable = null; |
| /** |
| * Overwrite all flag. |
| */ |
| private boolean alwaysOverwrite = false; |
| |
| private String[] modelProviderIds; |
| |
| /** |
| * Returns a new name for a copy of the resource at the given path in the |
| * given workspace. This name is determined automatically. |
| * |
| * @param originalName |
| * the full path of the resource |
| * @param workspace |
| * the workspace |
| * @return the new full path for the copy |
| */ |
| static IPath getAutoNewNameFor(IPath originalName, IWorkspace workspace) { |
| String resourceName = originalName.lastSegment(); |
| IPath leadupSegment = originalName.removeLastSegments(1); |
| boolean isFile = !originalName.hasTrailingSeparator(); |
| |
| String newName = computeNewName(resourceName, isFile); |
| while (true) { |
| IPath pathToTry = leadupSegment.append(newName); |
| if (!workspace.getRoot().exists(pathToTry)) { |
| return pathToTry; |
| } |
| newName = computeNewName(newName, isFile); |
| } |
| } |
| |
| private static String computeNewName(String str, boolean isFile) { |
| int lastIndexOfDot = str.lastIndexOf('.'); |
| String fileExtension = ""; //$NON-NLS-1$ |
| String fileNameNoExtension = str; |
| if (isFile && lastIndexOfDot > 0) { |
| fileExtension = str.substring(lastIndexOfDot); |
| fileNameNoExtension = str.substring(0, lastIndexOfDot); |
| } |
| Pattern p = Pattern.compile("[0-9]+$"); //$NON-NLS-1$ |
| Matcher m = p.matcher(fileNameNoExtension); |
| if (m.find()) { |
| // String ends with a number: increment it by 1 |
| int newNumber = Integer.parseInt(m.group()) + 1; |
| String numberStr = m.replaceFirst(Integer.toString(newNumber)); |
| return numberStr + fileExtension; |
| } |
| return fileNameNoExtension + "2" + fileExtension; //$NON-NLS-1$ |
| } |
| |
| /** |
| * Creates a new operation initialized with a shell. |
| * |
| * @param shell |
| * parent shell for error dialogs |
| */ |
| public CopyFilesAndFoldersOperation(Shell shell) { |
| messageShell = shell; |
| } |
| |
| /** |
| * Returns whether this operation is able to perform on-the-fly |
| * auto-renaming of resources with name collisions. |
| * |
| * @return <code>true</code> if auto-rename is supported, and |
| * <code>false</code> otherwise |
| */ |
| protected boolean canPerformAutoRename() { |
| return true; |
| } |
| |
| /** |
| * Returns the message for querying deep copy/move of a linked resource. |
| * |
| * @param source |
| * resource the query is made for |
| * @return the deep query message |
| */ |
| protected String getDeepCheckQuestion(IResource source) { |
| return NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_deepCopyQuestion, |
| source.getFullPath().makeRelative()); |
| } |
| |
| /** |
| * Checks whether the infos exist. |
| * |
| * @param stores |
| * the file infos to test |
| * @return Multi status with one error message for each missing file. |
| */ |
| IStatus checkExist(IFileStore[] stores) { |
| MultiStatus multiStatus = new MultiStatus(PlatformUI.PLUGIN_ID, |
| IStatus.OK, getProblemsMessage(), null); |
| |
| for (IFileStore store : stores) { |
| if (store.fetchInfo().exists() == false) { |
| String message = NLS.bind(IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceDeleted, |
| store.getName()); |
| IStatus status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK, message, null); |
| multiStatus.add(status); |
| } |
| } |
| return multiStatus; |
| } |
| |
| /** |
| * Checks whether the resources with the given names exist. |
| * |
| * @param resources |
| * IResources to checl |
| * @return Multi status with one error message for each missing file. |
| */ |
| IStatus checkExist(IResource[] resources) { |
| MultiStatus multiStatus = new MultiStatus(PlatformUI.PLUGIN_ID, |
| IStatus.OK, getProblemsMessage(), null); |
| |
| for (IResource resource : resources) { |
| if (resource != null && !resource.isVirtual()) { |
| URI location = resource.getLocationURI(); |
| String message = null; |
| if (location != null) { |
| IFileInfo info = IDEResourceInfoUtils.getFileInfo(location); |
| if (info == null || info.exists() == false) { |
| if (resource.isLinked()) { |
| message = NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_missingLinkTarget, |
| resource.getName()); |
| } else { |
| message = NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceDeleted, |
| resource.getName()); |
| } |
| } |
| } |
| if (message != null) { |
| IStatus status = new Status(IStatus.ERROR, |
| PlatformUI.PLUGIN_ID, IStatus.OK, message, null); |
| multiStatus.add(status); |
| } |
| } |
| } |
| return multiStatus; |
| } |
| |
| /** |
| * Check if the user wishes to overwrite the supplied resource or all |
| * resources. |
| * |
| * @param source |
| * the source resource |
| * @param destination |
| * the resource to be overwritten |
| * @return one of IDialogConstants.YES_ID, IDialogConstants.YES_TO_ALL_ID, |
| * IDialogConstants.NO_ID, IDialogConstants.CANCEL_ID indicating |
| * whether the current resource or all resources can be overwritten, |
| * or if the operation should be canceled. |
| */ |
| private int checkOverwrite(final IResource source, |
| final IResource destination) { |
| final int[] result = new int[1]; |
| |
| // Dialogs need to be created and opened in the UI thread |
| Runnable query = new Runnable() { |
| @Override |
| public void run() { |
| String message; |
| int resultId[] = { IDialogConstants.YES_ID, |
| IDialogConstants.YES_TO_ALL_ID, IDialogConstants.NO_ID, |
| IDialogConstants.CANCEL_ID }; |
| String labels[] = new String[] { IDialogConstants.YES_LABEL, |
| IDialogConstants.YES_TO_ALL_LABEL, |
| IDialogConstants.NO_LABEL, |
| IDialogConstants.CANCEL_LABEL }; |
| |
| if (destination.getType() == IResource.FOLDER) { |
| if (homogenousResources(source, destination)) { |
| message = NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteMergeQuestion, |
| destination.getFullPath() |
| .makeRelative()); |
| } else { |
| if (destination.isLinked()) { |
| message = NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteNoMergeLinkQuestion, |
| destination.getFullPath() |
| .makeRelative()); |
| } else { |
| message = NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteNoMergeNoLinkQuestion, |
| destination.getFullPath() |
| .makeRelative()); |
| } |
| resultId = new int[] { IDialogConstants.YES_ID, |
| IDialogConstants.NO_ID, |
| IDialogConstants.CANCEL_ID }; |
| labels = new String[] { IDialogConstants.YES_LABEL, |
| IDialogConstants.NO_LABEL, |
| IDialogConstants.CANCEL_LABEL }; |
| } |
| } else { |
| String[] bindings = new String[] { |
| IDEResourceInfoUtils.getLocationText(destination), |
| IDEResourceInfoUtils |
| .getDateStringValue(destination), |
| IDEResourceInfoUtils.getLocationText(source), |
| IDEResourceInfoUtils.getDateStringValue(source) }; |
| message = NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteWithDetailsQuestion, |
| bindings); |
| } |
| MessageDialog dialog = new MessageDialog( |
| messageShell, |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceExists, |
| null, message, MessageDialog.QUESTION, 0, labels) { |
| @Override |
| protected int getShellStyle() { |
| return super.getShellStyle() | SWT.SHEET; |
| } |
| }; |
| dialog.open(); |
| if (dialog.getReturnCode() == SWT.DEFAULT) { |
| // A window close returns SWT.DEFAULT, which has to be |
| // mapped to a cancel |
| result[0] = IDialogConstants.CANCEL_ID; |
| } else { |
| result[0] = resultId[dialog.getReturnCode()]; |
| } |
| } |
| }; |
| messageShell.getDisplay().syncExec(query); |
| return result[0]; |
| } |
| |
| /** |
| * Recursively collects existing files in the specified destination path. |
| * |
| * @param destinationPath |
| * destination path to check for existing files |
| * @param copyResources |
| * resources that may exist in the destination |
| * @param existing |
| * holds the collected existing files |
| */ |
| private void collectExistingReadonlyFiles(IPath destinationPath, |
| IResource[] copyResources, ArrayList existing) { |
| IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); |
| |
| for (IResource resource : copyResources) { |
| IPath newDestinationPath = destinationPath.append(resource.getName()); |
| IResource newDestination = workspaceRoot.findMember(newDestinationPath); |
| IFolder folder; |
| |
| if (newDestination == null) { |
| continue; |
| } |
| folder = getFolder(newDestination); |
| if (folder != null) { |
| IFolder sourceFolder = getFolder(resource); |
| |
| if (sourceFolder != null) { |
| try { |
| collectExistingReadonlyFiles(newDestinationPath, |
| sourceFolder.members(), existing); |
| } catch (CoreException exception) { |
| recordError(exception); |
| } |
| } |
| } else { |
| IFile file = getFile(newDestination); |
| |
| if (file != null) { |
| if (file.isReadOnly()) { |
| existing.add(file); |
| } |
| if (getValidateConflictSource()) { |
| IFile sourceFile = getFile(resource); |
| if (sourceFile != null) { |
| existing.add(sourceFile); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Copies the resources to the given destination. This method is called |
| * recursively to merge folders during folder copy. |
| * |
| * @param resources |
| * the resources to copy |
| * @param destination |
| * destination to which resources will be copied |
| * @param monitor |
| * a progress monitor for showing progress and for cancelation |
| * |
| * @deprecated As of 3.3, the work is performed in the undoable operation |
| * created in |
| * {@link #getUndoableCopyOrMoveOperation(IResource[], IPath)} |
| */ |
| @Deprecated |
| protected void copy(IResource[] resources, IPath destination, IProgressMonitor monitor) throws CoreException { |
| SubMonitor subMonitor = SubMonitor.convert(monitor, |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_CopyResourcesTask, resources.length); |
| |
| for (IResource resource : resources) { |
| SubMonitor iterationMonitor = subMonitor.split(1).setWorkRemaining(100); |
| IPath destinationPath = destination.append(resource.getName()); |
| IWorkspace workspace = resource.getWorkspace(); |
| IWorkspaceRoot workspaceRoot = workspace.getRoot(); |
| IResource existing = workspaceRoot.findMember(destinationPath); |
| if (resource.getType() == IResource.FOLDER && existing != null) { |
| // the resource is a folder and it exists in the destination, |
| // copy the |
| // children of the folder. |
| if (homogenousResources(resource, existing)) { |
| IResource[] children = ((IContainer) resource).members(); |
| copy(children, destinationPath, iterationMonitor.split(100)); |
| } else { |
| // delete the destination folder, copying a linked folder |
| // over an unlinked one or vice versa. Fixes bug 28772. |
| delete(existing, iterationMonitor.split(10)); |
| resource.copy(destinationPath, IResource.SHALLOW, iterationMonitor.split(90)); |
| } |
| } else { |
| if (existing != null) { |
| if (homogenousResources(resource, existing)) { |
| copyExisting(resource, existing, iterationMonitor.split(100)); |
| } else { |
| if (existing != null) { |
| // Copying a linked resource over unlinked or vice |
| // versa. |
| // Can't use setContents here. Fixes bug 28772. |
| delete(existing, iterationMonitor.split(10)); |
| } |
| iterationMonitor.setWorkRemaining(100); |
| |
| if ((createLinks || createVirtualFoldersAndLinks) && (resource.isLinked() == false) |
| && (resource.isVirtual() == false)) { |
| if (resource.getType() == IResource.FILE) { |
| IFile file = workspaceRoot.getFile(destinationPath); |
| file.createLink(createRelativePath(resource.getLocationURI(), file), 0, |
| iterationMonitor.split(100)); |
| } else { |
| IFolder folder = workspaceRoot.getFolder(destinationPath); |
| if (createVirtualFoldersAndLinks) { |
| folder.create(IResource.VIRTUAL, true, iterationMonitor.split(1)); |
| IResource[] members = ((IContainer) resource).members(); |
| if (members.length > 0) |
| copy(members, destinationPath, iterationMonitor.split(99)); |
| } else |
| folder.createLink(createRelativePath(resource.getLocationURI(), folder), 0, |
| iterationMonitor.split(100)); |
| } |
| } else |
| resource.copy(destinationPath, IResource.SHALLOW, iterationMonitor.split(100)); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Transform an absolute path URI to a relative path one (i.e. from |
| * "C:\foo\bar\file.txt" to "VAR\file.txt" granted that the relativeVariable |
| * is "VAR" and points to "C:\foo\bar\"). |
| * |
| * @param locationURI |
| * @return an URI that was made relative to a variable |
| */ |
| private URI createRelativePath(URI locationURI, IResource resource) { |
| if (relativeVariable == null) |
| return locationURI; |
| IPath location = URIUtil.toPath(locationURI); |
| IPath result; |
| try { |
| result = URIUtil.toPath(resource.getPathVariableManager().convertToRelative(URIUtil.toURI(location), true, relativeVariable)); |
| } catch (CoreException e) { |
| return locationURI; |
| } |
| return URIUtil.toURI(result); |
| } |
| |
| /** |
| * Sets the content of the existing file to the source file content. |
| * |
| * @param source |
| * source file to copy |
| * @param existing |
| * existing file to set the source content in |
| * @param subMonitor |
| * a progress monitor for showing progress and for cancelation |
| * @throws CoreException |
| * setContents failed |
| */ |
| private void copyExisting(IResource source, IResource existing, IProgressMonitor monitor) throws CoreException { |
| SubMonitor subMonitor = SubMonitor.convert(monitor, 1); |
| IFile existingFile = getFile(existing); |
| |
| if (existingFile != null) { |
| IFile sourceFile = getFile(source); |
| |
| if (sourceFile != null) { |
| existingFile.setContents(sourceFile.getContents(), IResource.KEEP_HISTORY, subMonitor.split(1)); |
| } |
| } |
| } |
| |
| /** |
| * Copies the given resources to the destination. The current Thread is |
| * halted while the resources are copied using a WorkspaceModifyOperation. |
| * This method should be called from the UIThread. |
| * |
| * @param resources |
| * the resources to copy |
| * @param destination |
| * destination to which resources will be copied |
| * @return the resources which actually got copied |
| * @see WorkspaceModifyOperation |
| * @see Display#getThread() |
| * @see Thread#currentThread() |
| */ |
| public IResource[] copyResources(final IResource[] resources, |
| IContainer destination) { |
| return copyResources(resources, destination, true); |
| } |
| |
| /** |
| * Copies the given resources to the destination in the current Thread |
| * without forking a new Thread or blocking using a |
| * WorkspaceModifyOperation. It recommended that this method only be called |
| * from a {@link WorkspaceJob} to avoid possible deadlock. |
| * |
| * @param resources |
| * the resources to copy |
| * @param destination |
| * destination to which resources will be copied |
| * @param monitor |
| * the monitor that information will be sent to. |
| * @return IResource[] the resulting {@link IResource}[] |
| * @see WorkspaceModifyOperation |
| * @see WorkspaceJob |
| * @since 3.2 |
| */ |
| public IResource[] copyResourcesInCurrentThread( |
| final IResource[] resources, IContainer destination, |
| IProgressMonitor monitor) { |
| return copyResources(resources, destination, false); |
| } |
| |
| /** |
| * Copies the given resources to the destination. |
| * |
| * @param resources |
| * the resources to copy |
| * @param destination |
| * destination to which resources will be copied |
| * @return IResource[] the resulting {@link IResource}[] |
| */ |
| private IResource[] copyResources(final IResource[] resources, |
| IContainer destination, boolean fork) { |
| final IPath destinationPath = destination.getFullPath(); |
| final IResource[][] copiedResources = new IResource[1][0]; |
| |
| // test resources for existence separate from validate API. |
| // Validate is performance critical and resource exists |
| // check is potentially slow. Fixes bugs 16129/28602. |
| IStatus resourceStatus = checkExist(resources); |
| if (resourceStatus.getSeverity() != IStatus.OK) { |
| displayError(resourceStatus); |
| return copiedResources[0]; |
| } |
| String errorMsg = validateDestination(destination, resources); |
| if (errorMsg != null) { |
| displayError(errorMsg); |
| return copiedResources[0]; |
| } |
| |
| IRunnableWithProgress op = new IRunnableWithProgress() { |
| @Override |
| public void run(IProgressMonitor monitor) { |
| copyResources(resources, destinationPath, copiedResources, |
| monitor); |
| } |
| }; |
| |
| try { |
| PlatformUI.getWorkbench().getProgressService().run(fork, true, op); |
| } catch (InterruptedException e) { |
| return copiedResources[0]; |
| } catch (InvocationTargetException e) { |
| display(e); |
| } |
| |
| // If errors occurred, open an Error dialog |
| if (errorStatus != null) { |
| displayError(errorStatus); |
| errorStatus = null; |
| } |
| |
| return copiedResources[0]; |
| } |
| |
| /** |
| * Return whether the operation is a move or a copy |
| * |
| * @return whether the operation is a move or a copy |
| * @since 3.2 |
| */ |
| protected boolean isMove() { |
| return false; |
| } |
| |
| private void display(InvocationTargetException e) { |
| // CoreExceptions are collected above, but unexpected runtime |
| // exceptions and errors may still occur. |
| IDEWorkbenchPlugin.getDefault().getLog().log( |
| StatusUtil.newStatus(IStatus.ERROR, MessageFormat.format( |
| "Exception in {0}.performCopy(): {1}", //$NON-NLS-1$ |
| getClass().getName(), e.getTargetException()), null)); |
| displayError(NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_internalError, |
| e.getTargetException().getMessage())); |
| } |
| |
| /** |
| * Copies the given URIS and folders to the destination. The current Thread |
| * is halted while the resources are copied using a |
| * WorkspaceModifyOperation. This method should be called from the UI |
| * Thread. |
| * |
| * @param uris |
| * the URIs to copy |
| * @param destination |
| * destination to which files will be copied |
| * @see WorkspaceModifyOperation |
| * @see Display#getThread() |
| * @see Thread#currentThread() |
| * @since 3.2 |
| */ |
| public void copyFiles(URI[] uris, IContainer destination) { |
| IFileStore[] stores = buildFileStores(uris); |
| if (stores == null) { |
| return; |
| } |
| |
| copyFileStores(destination, stores, true, null); |
| } |
| |
| /** |
| * Copies the given files and folders to the destination without forking a |
| * new Thread or blocking using a WorkspaceModifyOperation. It is |
| * recommended that this method only be called from a {@link WorkspaceJob} |
| * to avoid possible deadlock. |
| * |
| * @param uris |
| * the URIs to copy |
| * @param destination |
| * destination to which URIS will be copied |
| * @param monitor |
| * the monitor that information will be sent to. |
| * @see WorkspaceModifyOperation |
| * @see WorkspaceJob |
| * @since 3.2 |
| */ |
| public void copyFilesInCurrentThread(URI[] uris, IContainer destination, |
| IProgressMonitor monitor) { |
| IFileStore[] stores = buildFileStores(uris); |
| if (stores == null) { |
| return; |
| } |
| |
| copyFileStores(destination, stores, false, monitor); |
| } |
| |
| /** |
| * Build the collection of fileStores that map to fileNames. If any of them |
| * cannot be found then match then return <code>null</code>. |
| * |
| * @param uris |
| * @return IFileStore[] |
| */ |
| private IFileStore[] buildFileStores(URI[] uris) { |
| IFileStore[] stores = new IFileStore[uris.length]; |
| for (int i = 0; i < uris.length; i++) { |
| IFileStore store; |
| try { |
| store = EFS.getStore(uris[i]); |
| } catch (CoreException e) { |
| StatusManager.getManager().handle(e, IDEWorkbenchPlugin.IDE_WORKBENCH); |
| reportFileInfoNotFound(uris[i].toString()); |
| return null; |
| } |
| if (store == null) { |
| reportFileInfoNotFound(uris[i].toString()); |
| return null; |
| } |
| stores[i] = store; |
| } |
| return stores; |
| |
| } |
| |
| /** |
| * Depending on the 'Linked Resources' preferences it copies the given files and folders to the |
| * destination or creates links or shows a dialog that lets the user choose. The current thread |
| * is halted while the resources are copied using a {@link WorkspaceModifyOperation}. This |
| * method should be called from the UI Thread. |
| * |
| * @param fileNames names of the files to copy |
| * @param destination destination to which files will be copied |
| * @param dropOperation the drop operation ({@link DND#DROP_NONE}, {@link DND#DROP_MOVE} |
| * {@link DND#DROP_COPY}, {@link DND#DROP_LINK}, {@link DND#DROP_DEFAULT}) |
| * @see WorkspaceModifyOperation |
| * @see Display#getThread() |
| * @see Thread#currentThread() |
| * @since 3.6 |
| */ |
| public void copyOrLinkFiles(final String[] fileNames, IContainer destination, int dropOperation) { |
| IPreferenceStore store= IDEWorkbenchPlugin.getDefault().getPreferenceStore(); |
| boolean targetIsVirtual= destination.isVirtual(); |
| String dndPreference= store.getString(targetIsVirtual ? IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_VIRTUAL_FOLDER_MODE : IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_MODE); |
| |
| int mode= ImportTypeDialog.IMPORT_NONE; |
| String variable= null; |
| |
| //check if resource linking is disabled |
| if (ResourcesPlugin.getPlugin().getPluginPreferences().getBoolean(ResourcesPlugin.PREF_DISABLE_LINKING)) |
| mode= ImportTypeDialog.IMPORT_COPY; |
| else { |
| if (dndPreference.equals(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_MODE_PROMPT)) { |
| ImportTypeDialog dialog= new ImportTypeDialog(messageShell, dropOperation, fileNames, destination); |
| dialog.setResource(destination); |
| if (dialog.open() == Window.OK) { |
| mode= dialog.getSelection(); |
| variable= dialog.getVariable(); |
| } |
| } else if (dndPreference.equals(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_MODE_MOVE_COPY)) { |
| mode= ImportTypeDialog.IMPORT_COPY; |
| } else if (dndPreference.equals(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_MODE_LINK)) { |
| mode= ImportTypeDialog.IMPORT_LINK; |
| } else if (dndPreference.equals(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_MODE_LINK_AND_VIRTUAL_FOLDER)) { |
| mode= ImportTypeDialog.IMPORT_VIRTUAL_FOLDERS_AND_LINKS; |
| } |
| } |
| |
| switch (mode) { |
| case ImportTypeDialog.IMPORT_COPY: |
| copyFiles(fileNames, destination); |
| break; |
| case ImportTypeDialog.IMPORT_VIRTUAL_FOLDERS_AND_LINKS: |
| if (variable != null) |
| setRelativeVariable(variable); |
| createVirtualFoldersAndLinks(fileNames, destination); |
| break; |
| case ImportTypeDialog.IMPORT_LINK: |
| if (variable != null) |
| setRelativeVariable(variable); |
| linkFiles(fileNames, destination); |
| break; |
| case ImportTypeDialog.IMPORT_NONE: |
| break; |
| } |
| |
| } |
| |
| /** |
| * Copies the given files and folders to the destination. The current Thread is halted while the |
| * resources are copied using a WorkspaceModifyOperation. This method should be called from the |
| * UI Thread. |
| * |
| * @param fileNames names of the files to copy |
| * @param destination destination to which files will be copied |
| * @see WorkspaceModifyOperation |
| * @see Display#getThread() |
| * @see Thread#currentThread() |
| * @since 3.2 |
| */ |
| public void copyFiles(final String[] fileNames, IContainer destination) { |
| IFileStore[] stores = buildFileStores(fileNames); |
| if (stores == null) { |
| return; |
| } |
| |
| copyFileStores(destination, stores, true, null); |
| } |
| |
| /** |
| * Copies the given files and folders to the destination without forking a |
| * new Thread or blocking using a WorkspaceModifyOperation. It is |
| * recommended that this method only be called from a {@link WorkspaceJob} |
| * to avoid possible deadlock. |
| * |
| * @param fileNames |
| * names of the files to copy |
| * @param destination |
| * destination to which files will be copied |
| * @param monitor |
| * the monitor that information will be sent to. |
| * @see WorkspaceModifyOperation |
| * @see WorkspaceJob |
| * @since 3.2 |
| */ |
| public void copyFilesInCurrentThread(final String[] fileNames, |
| IContainer destination, IProgressMonitor monitor) { |
| IFileStore[] stores = buildFileStores(fileNames); |
| if (stores == null) { |
| return; |
| } |
| |
| copyFileStores(destination, stores, false, monitor); |
| } |
| |
| /** |
| * Build the collection of fileStores that map to fileNames. If any of them |
| * cannot be found then match then return null. |
| * |
| * @param fileNames |
| * @return IFileStore[] |
| */ |
| private IFileStore[] buildFileStores(final String[] fileNames) { |
| IFileStore[] stores = new IFileStore[fileNames.length]; |
| for (int i = 0; i < fileNames.length; i++) { |
| IFileStore store = IDEResourceInfoUtils.getFileStore(fileNames[i]); |
| if (store == null) { |
| reportFileInfoNotFound(fileNames[i]); |
| return null; |
| } |
| stores[i] = store; |
| } |
| return stores; |
| } |
| |
| /** |
| * Report that a file info could not be found. |
| * |
| * @param fileName |
| */ |
| private void reportFileInfoNotFound(final String fileName) { |
| |
| messageShell.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| ErrorDialog |
| .openError( |
| messageShell, |
| getProblemsTitle(), |
| NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_infoNotFound, |
| fileName), null); |
| } |
| }); |
| } |
| |
| /** |
| * Copies the given files and folders to the destination. |
| * |
| * @param stores |
| * the file stores to copy |
| * @param destination |
| * destination to which files will be copied |
| */ |
| private void copyFileStores(IContainer destination, |
| final IFileStore[] stores, boolean fork, IProgressMonitor monitor) { |
| // test files for existence separate from validate API |
| // because an external file may not exist until the copy actually |
| // takes place (e.g., WinZip contents). |
| IStatus fileStatus = checkExist(stores); |
| if (fileStatus.getSeverity() != IStatus.OK) { |
| displayError(fileStatus); |
| return; |
| } |
| String errorMsg = validateImportDestinationInternal(destination, stores); |
| if (errorMsg != null) { |
| displayError(errorMsg); |
| return; |
| } |
| final IPath destinationPath = destination.getFullPath(); |
| |
| if (fork) { |
| WorkspaceModifyOperation op = new WorkspaceModifyOperation() { |
| @Override |
| public void execute(IProgressMonitor monitor) { |
| copyFileStores(stores, destinationPath, monitor); |
| } |
| }; |
| try { |
| PlatformUI.getWorkbench().getProgressService().run(true, true, |
| op); |
| } catch (InterruptedException e) { |
| return; |
| } catch (InvocationTargetException exception) { |
| display(exception); |
| } |
| } else { |
| copyFileStores(stores, destinationPath, monitor); |
| } |
| |
| // If errors occurred, open an Error dialog |
| if (errorStatus != null) { |
| displayError(errorStatus); |
| errorStatus = null; |
| } |
| } |
| |
| /** |
| * Display the supplied status in an error dialog. |
| * |
| * @param status |
| * The status to display |
| */ |
| private void displayError(final IStatus status) { |
| messageShell.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| ErrorDialog.openError(messageShell, getProblemsTitle(), null, |
| status); |
| } |
| }); |
| } |
| |
| /** |
| * Creates a file or folder handle for the source resource as if it were to |
| * be created in the destination container. |
| * |
| * @param destination |
| * destination container |
| * @param source |
| * source resource |
| * @return IResource file or folder handle, depending on the source type. |
| */ |
| IResource createLinkedResourceHandle(IContainer destination, |
| IResource source) { |
| IWorkspace workspace = destination.getWorkspace(); |
| IWorkspaceRoot workspaceRoot = workspace.getRoot(); |
| IPath linkPath = destination.getFullPath().append(source.getName()); |
| IResource linkHandle; |
| |
| if (source.getType() == IResource.FOLDER) { |
| linkHandle = workspaceRoot.getFolder(linkPath); |
| } else { |
| linkHandle = workspaceRoot.getFile(linkPath); |
| } |
| return linkHandle; |
| } |
| |
| /** |
| * Removes the given resource from the workspace. |
| * |
| * @param resource |
| * resource to remove from the workspace |
| * @param monitor |
| * a progress monitor for showing progress and for cancelation |
| * @return true the resource was deleted successfully false the resource was |
| * not deleted because a CoreException occurred |
| */ |
| boolean delete(IResource resource, IProgressMonitor monitor) { |
| boolean force = false; // don't force deletion of out-of-sync resources |
| |
| if (resource.getType() == IResource.PROJECT) { |
| // if it's a project, ask whether content should be deleted too |
| IProject project = (IProject) resource; |
| try { |
| project.delete(true, force, monitor); |
| } catch (CoreException e) { |
| recordError(e); // log error |
| return false; |
| } |
| } else { |
| // if it's not a project, just delete it |
| int flags = IResource.KEEP_HISTORY; |
| if (force) { |
| flags = flags | IResource.FORCE; |
| } |
| try { |
| resource.delete(flags, monitor); |
| } catch (CoreException e) { |
| recordError(e); // log error |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Opens an error dialog to display the given message. |
| * |
| * @param message |
| * the error message to show |
| */ |
| private void displayError(final String message) { |
| messageShell.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| MessageDialog.openError(messageShell, getProblemsTitle(), |
| message); |
| } |
| }); |
| } |
| |
| /** |
| * Returns the resource either casted to or adapted to an IFile. |
| * |
| * @param resource |
| * resource to cast/adapt |
| * @return the resource either casted to or adapted to an IFile. |
| * <code>null</code> if the resource does not adapt to IFile |
| */ |
| protected IFile getFile(IResource resource) { |
| return Adapters.adapt(resource, IFile.class); |
| } |
| |
| /** |
| * Returns java.io.File objects for the given file names. |
| * |
| * @param fileNames |
| * files to return File object for. |
| * @return java.io.File objects for the given file names. |
| * @deprecated As of 3.3, this method is no longer in use anywhere in this |
| * class and is only provided for backwards compatability with |
| * subclasses of the receiver. |
| */ |
| @Deprecated |
| protected File[] getFiles(String[] fileNames) { |
| File[] files = new File[fileNames.length]; |
| |
| for (int i = 0; i < fileNames.length; i++) { |
| files[i] = new File(fileNames[i]); |
| } |
| return files; |
| } |
| |
| /** |
| * Returns the resource either casted to or adapted to an IFolder. |
| * |
| * @param resource |
| * resource to cast/adapt |
| * @return the resource either casted to or adapted to an IFolder. |
| * <code>null</code> if the resource does not adapt to IFolder |
| */ |
| protected IFolder getFolder(IResource resource) { |
| return Adapters.adapt(resource, IFolder.class); |
| } |
| |
| /** |
| * Returns a new name for a copy of the resource at the given path in the |
| * given workspace. |
| * |
| * @param originalName |
| * the full path of the resource |
| * @param workspace |
| * the workspace |
| * @return the new full path for the copy, or <code>null</code> if the |
| * resource should not be copied |
| */ |
| private IPath getNewNameFor(final IPath originalName, |
| final IWorkspace workspace) { |
| final IResource resource = workspace.getRoot().findMember(originalName); |
| final IPath prefix = resource.getFullPath().removeLastSegments(1); |
| final String returnValue[] = { "" }; //$NON-NLS-1$ |
| |
| messageShell.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| IInputValidator validator = new IInputValidator() { |
| @Override |
| public String isValid(String string) { |
| if (resource.getName().equals(string)) { |
| return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_nameMustBeDifferent; |
| } |
| IStatus status = workspace.validateName(string, |
| resource.getType()); |
| if (!status.isOK()) { |
| return status.getMessage(); |
| } |
| if (workspace.getRoot().exists(prefix.append(string))) { |
| return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_nameExists; |
| } |
| return null; |
| } |
| }; |
| |
| final String initial = getAutoNewNameFor(originalName, workspace).lastSegment().toString(); |
| InputDialog dialog = new InputDialog( |
| messageShell, |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_inputDialogTitle, |
| NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_inputDialogMessage, |
| resource.getName()), initial, validator) { |
| |
| @Override |
| protected Control createContents(Composite parent) { |
| Control contents= super.createContents(parent); |
| int lastIndexOfDot= initial.lastIndexOf('.'); |
| if (resource instanceof IFile && lastIndexOfDot > 0) { |
| getText().setSelection(0, lastIndexOfDot); |
| } |
| return contents; |
| } |
| }; |
| dialog.setBlockOnOpen(true); |
| dialog.open(); |
| if (dialog.getReturnCode() == Window.CANCEL) { |
| returnValue[0] = null; |
| } else { |
| returnValue[0] = dialog.getValue(); |
| } |
| } |
| }); |
| if (returnValue[0] == null) { |
| throw new OperationCanceledException(); |
| } |
| return prefix.append(returnValue[0]); |
| } |
| |
| /** |
| * Returns the task title for this operation's progress dialog. |
| * |
| * @return the task title |
| */ |
| protected String getOperationTitle() { |
| return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_operationTitle; |
| } |
| |
| /** |
| * Returns the message for this operation's problems dialog. |
| * |
| * @return the problems message |
| */ |
| protected String getProblemsMessage() { |
| return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_problemMessage; |
| } |
| |
| /** |
| * Returns the title for this operation's problems dialog. |
| * |
| * @return the problems dialog title |
| */ |
| protected String getProblemsTitle() { |
| return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyFailedTitle; |
| } |
| |
| /** |
| * Returns whether the source file in a destination collision will be |
| * validateEdited together with the collision itself. Returns false. Should |
| * return true if the source file is to be deleted after the operation. |
| * |
| * @return boolean <code>true</code> if the source file in a destination |
| * collision should be validateEdited. <code>false</code> if only |
| * the destination should be validated. |
| */ |
| protected boolean getValidateConflictSource() { |
| return false; |
| } |
| |
| /** |
| * Returns whether the given resources are either both linked or both |
| * unlinked. |
| * |
| * @param source |
| * source resource |
| * @param destination |
| * destination resource |
| * @return boolean <code>true</code> if both resources are either linked |
| * or unlinked. <code>false</code> otherwise. |
| */ |
| protected boolean homogenousResources(IResource source, |
| IResource destination) { |
| boolean isSourceLinked = source.isLinked(); |
| boolean isDestinationLinked = destination.isLinked(); |
| |
| return (isSourceLinked && isDestinationLinked || isSourceLinked == false |
| && isDestinationLinked == false); |
| } |
| |
| /** |
| * Returns whether the given resource is accessible. Files and folders are |
| * always considered accessible and a project is accessible if it is open. |
| * |
| * @param resource |
| * the resource |
| * @return <code>true</code> if the resource is accessible, and |
| * <code>false</code> if it is not |
| */ |
| private boolean isAccessible(IResource resource) { |
| switch (resource.getType()) { |
| case IResource.FILE: |
| return true; |
| case IResource.FOLDER: |
| return true; |
| case IResource.PROJECT: |
| return ((IProject) resource).isOpen(); |
| default: |
| return false; |
| } |
| } |
| |
| /** |
| * Returns whether any of the given source resources are being recopied to |
| * their current container. |
| * |
| * @param sourceResources |
| * the source resources |
| * @param destination |
| * the destination container |
| * @return <code>true</code> if at least one of the given source |
| * resource's parent container is the same as the destination |
| */ |
| boolean isDestinationSameAsSource(IResource[] sourceResources, |
| IContainer destination) { |
| IPath destinationLocation = destination.getLocation(); |
| |
| for (IResource resource : sourceResources) { |
| if (resource.getParent().equals(destination)) { |
| return true; |
| } else if (destinationLocation != null) { |
| // do thorough check to catch linked resources. Fixes bug 29913. |
| IPath sourceLocation = resource.getLocation(); |
| IPath destinationResource = destinationLocation.append(resource.getName()); |
| if (sourceLocation != null |
| && sourceLocation.isPrefixOf(destinationResource)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Copies the given resources to the destination container with the given |
| * name. |
| * <p> |
| * Note: the destination container may need to be created prior to copying |
| * the resources. |
| * </p> |
| * |
| * @param resources |
| * the resources to copy |
| * @param destination |
| * the path of the destination container |
| * @param monitor |
| * a progress monitor for showing progress and for cancelation |
| * @return <code>true</code> if the copy operation completed without |
| * errors |
| */ |
| private boolean performCopy(IResource[] resources, IPath destination, |
| IProgressMonitor monitor) { |
| try { |
| AbstractWorkspaceOperation op = getUndoableCopyOrMoveOperation( |
| resources, destination); |
| op.setModelProviderIds(getModelProviderIds()); |
| if (op instanceof CopyResourcesOperation) { |
| CopyResourcesOperation copyMoveOp = (CopyResourcesOperation) op; |
| copyMoveOp.setCreateVirtualFolders(createVirtualFoldersAndLinks); |
| copyMoveOp.setCreateLinks(createLinks); |
| copyMoveOp.setRelativeVariable(relativeVariable); |
| } |
| // If we are copying files and folders, do not execute the operation |
| // in the undo history, since the creation of a new file is not |
| // added to undo history and modification of a file is not added to |
| // the same undo history and therefore a redo cannot be properly |
| // done. Just execute it directly so it won't be added to the undo |
| // history. |
| op.execute(monitor, WorkspaceUndoUtil.getUIInfoAdapter(messageShell)); |
| } catch (ExecutionException e) { |
| if (e.getCause() instanceof CoreException) { |
| recordError((CoreException) e.getCause()); |
| } else { |
| IDEWorkbenchPlugin.log(e.getMessage(), e); |
| displayError(e.getMessage()); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Individually copies the given resources to the specified destination |
| * container checking for name collisions. If a collision is detected, it is |
| * saved with a new name. |
| * <p> |
| * Note: the destination container may need to be created prior to copying |
| * the resources. |
| * </p> |
| * |
| * @param resources |
| * the resources to copy |
| * @param destination |
| * the path of the destination container |
| * @return <code>true</code> if the copy operation completed without |
| * errors. |
| */ |
| private boolean performCopyWithAutoRename(IResource[] resources, |
| IPath destination, IProgressMonitor monitor) { |
| IWorkspace workspace = resources[0].getWorkspace(); |
| IPath[] destinationPaths = new IPath[resources.length]; |
| try { |
| for (int i = 0; i < resources.length; i++) { |
| IResource source = resources[i]; |
| destinationPaths[i] = destination.append(source.getName()); |
| if (source.getType() != IResource.FILE) { |
| destinationPaths[i] = destinationPaths[i].addTrailingSeparator(); |
| } |
| |
| if (workspace.getRoot().exists(destinationPaths[i])) { |
| destinationPaths[i] = getNewNameFor(destinationPaths[i], |
| workspace); |
| } |
| } |
| CopyResourcesOperation op = new CopyResourcesOperation(resources, |
| destinationPaths, |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyTitle); |
| op.setModelProviderIds(getModelProviderIds()); |
| // If we are copying files and folders, do not execute the operation |
| // in the undo history, since the creation of a new file is not |
| // added to undo history and modification of a file is not added to |
| // the same undo history and therefore a redo cannot be properly |
| // done. Just execute it directly so it won't be added to the undo |
| // history. |
| op.execute(monitor, WorkspaceUndoUtil.getUIInfoAdapter(messageShell)); |
| } catch (ExecutionException e) { |
| if (e.getCause() instanceof CoreException) { |
| recordError((CoreException) e.getCause()); |
| } else { |
| IDEWorkbenchPlugin.log(e.getMessage(), e); |
| displayError(e.getMessage()); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Performs an import of the given stores into the provided container. |
| * Returns a status indicating if the import was successful. |
| * |
| * @param stores |
| * stores that are to be imported |
| * @param target |
| * container to which the import will be done |
| * @param monitor |
| * a progress monitor for showing progress and for cancelation |
| */ |
| private void performFileImport(IFileStore[] stores, IContainer target, |
| IProgressMonitor monitor) { |
| IOverwriteQuery query = new IOverwriteQuery() { |
| @Override |
| public String queryOverwrite(String pathString) { |
| if (alwaysOverwrite) { |
| return ALL; |
| } |
| |
| final String returnCode[] = { CANCEL }; |
| final String msg = NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteQuestion, |
| pathString); |
| final String[] options = { IDialogConstants.YES_LABEL, |
| IDialogConstants.YES_TO_ALL_LABEL, |
| IDialogConstants.NO_LABEL, |
| IDialogConstants.CANCEL_LABEL }; |
| messageShell.getDisplay().syncExec(new Runnable() { |
| @Override |
| public void run() { |
| MessageDialog dialog = new MessageDialog( |
| messageShell, |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_question, |
| null, msg, MessageDialog.QUESTION, 0, options) { |
| @Override |
| protected int getShellStyle() { |
| return super.getShellStyle() | SWT.SHEET; |
| } |
| }; |
| dialog.open(); |
| int returnVal = dialog.getReturnCode(); |
| String[] returnCodes = { YES, ALL, NO, CANCEL }; |
| returnCode[0] = returnVal == -1 ? CANCEL |
| : returnCodes[returnVal]; |
| } |
| }); |
| if (returnCode[0] == ALL) { |
| alwaysOverwrite = true; |
| } else if (returnCode[0] == CANCEL) { |
| canceled = true; |
| } |
| return returnCode[0]; |
| } |
| }; |
| |
| ImportOperation op = new ImportOperation(target.getFullPath(), |
| stores[0].getParent(), FileStoreStructureProvider.INSTANCE, |
| query, Arrays.asList(stores)); |
| op.setContext(messageShell); |
| op.setCreateContainerStructure(false); |
| op.setVirtualFolders(createVirtualFoldersAndLinks); |
| op.setCreateLinks(createLinks); |
| op.setRelativeVariable(relativeVariable); |
| try { |
| op.run(monitor); |
| } catch (InterruptedException e) { |
| return; |
| } catch (InvocationTargetException e) { |
| if (e.getTargetException() instanceof CoreException) { |
| displayError(((CoreException) e.getTargetException()) |
| .getStatus()); |
| } else { |
| display(e); |
| } |
| return; |
| } |
| // Special case since ImportOperation doesn't throw a CoreException on |
| // failure. |
| IStatus status = op.getStatus(); |
| if (!status.isOK()) { |
| if (errorStatus == null) { |
| errorStatus = new MultiStatus(PlatformUI.PLUGIN_ID, |
| IStatus.ERROR, getProblemsMessage(), null); |
| } |
| errorStatus.merge(status); |
| } |
| } |
| |
| /** |
| * Records the core exception to be displayed to the user once the action is |
| * finished. |
| * |
| * @param error |
| * a <code>CoreException</code> |
| */ |
| private void recordError(CoreException error) { |
| if (errorStatus == null) { |
| errorStatus = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.ERROR, |
| getProblemsMessage(), error); |
| } |
| |
| errorStatus.merge(error.getStatus()); |
| } |
| |
| /** |
| * Checks whether the destination is valid for copying the source resources. |
| * <p> |
| * Note this method is for internal use only. It is not API. |
| * </p> |
| * |
| * @param destination |
| * the destination container |
| * @param sourceResources |
| * the source resources |
| * @return an error message, or <code>null</code> if the path is valid |
| */ |
| public String validateDestination(IContainer destination, |
| IResource[] sourceResources) { |
| if (!isAccessible(destination)) { |
| return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationAccessError; |
| } |
| IContainer firstParent = null; |
| URI destinationLocation = destination.getLocationURI(); |
| for (IResource sourceResource : sourceResources) { |
| if (firstParent == null) { |
| firstParent = sourceResource.getParent(); |
| } else if (firstParent.equals(sourceResource.getParent()) == false) { |
| // Resources must have common parent. Fixes bug 33398. |
| return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_parentNotEqual; |
| } |
| |
| // verify that if the destination is a virtual folder, the resource must be |
| // either a link or another virtual folder |
| if (destination.isVirtual()) { |
| if (!sourceResource.isLinked() && !sourceResource.isVirtual() |
| && !createLinks && !createVirtualFoldersAndLinks) { |
| return NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_sourceCannotBeCopiedIntoAVirtualFolder, |
| sourceResource.getName()); |
| } |
| } |
| URI sourceLocation = sourceResource.getLocationURI(); |
| if (sourceLocation == null) { |
| if (sourceResource.isLinked()) { |
| // Don't allow copying linked resources with undefined path |
| // variables. See bug 28754. |
| return NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_missingPathVariable, |
| sourceResource.getName()); |
| } |
| return NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceDeleted, |
| sourceResource.getName()); |
| |
| } |
| if (!destination.isVirtual()) { |
| if (sourceLocation.equals(destinationLocation)) { |
| return NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_sameSourceAndDest, |
| sourceResource.getName()); |
| } |
| // is the source a parent of the destination? |
| if (new Path(sourceLocation.toString()).isPrefixOf(new Path( |
| destinationLocation.toString()))) { |
| return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationDescendentError; |
| } |
| } |
| |
| String linkedResourceMessage = validateLinkedResource(destination, |
| sourceResource); |
| if (linkedResourceMessage != null) { |
| return linkedResourceMessage; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Validates that the given source resources can be copied to the |
| * destination as decided by the VCM provider. |
| * |
| * @param destination |
| * copy destination |
| * @param sourceResources |
| * source resources |
| * @return <code>true</code> all files passed validation or there were no |
| * files to validate. <code>false</code> one or more files did not |
| * pass validation. |
| */ |
| private boolean validateEdit(IContainer destination, |
| IResource[] sourceResources) { |
| ArrayList copyFiles = new ArrayList(); |
| |
| collectExistingReadonlyFiles(destination.getFullPath(), |
| sourceResources, copyFiles); |
| if (copyFiles.size() > 0) { |
| IFile[] files = (IFile[]) copyFiles.toArray(new IFile[copyFiles |
| .size()]); |
| IWorkspace workspace = ResourcesPlugin.getWorkspace(); |
| IStatus status = workspace.validateEdit(files, messageShell); |
| |
| canceled = status.isOK() == false; |
| return status.isOK(); |
| } |
| return true; |
| } |
| |
| /** |
| * Checks whether the destination is valid for copying the source files. |
| * <p> |
| * Note this method is for internal use only. It is not API. |
| * </p> |
| * |
| * @param destination |
| * the destination container |
| * @param sourceNames |
| * the source file names |
| * @return an error message, or <code>null</code> if the path is valid |
| */ |
| public String validateImportDestination(IContainer destination, |
| String[] sourceNames) { |
| |
| IFileStore[] stores = new IFileStore[sourceNames.length]; |
| for (int i = 0; i < sourceNames.length; i++) { |
| IFileStore store = IDEResourceInfoUtils |
| .getFileStore(sourceNames[i]); |
| if (store == null) { |
| return NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_infoNotFound, |
| sourceNames[i]); |
| } |
| stores[i] = store; |
| } |
| return validateImportDestinationInternal(destination, stores); |
| |
| } |
| |
| /** |
| * Checks whether the destination is valid for copying the source file |
| * stores. |
| * <p> |
| * Note this method is for internal use only. It is not API. |
| * </p> |
| * <p> |
| * TODO Bug 117804. This method has been renamed to avoid a bug in the |
| * Eclipse compiler with regards to visibility and type resolution when |
| * linking. |
| * </p> |
| * |
| * @param destination |
| * the destination container |
| * @param sourceStores |
| * the source IFileStore |
| * @return an error message, or <code>null</code> if the path is valid |
| */ |
| private String validateImportDestinationInternal(IContainer destination, |
| IFileStore[] sourceStores) { |
| if (!isAccessible(destination)) |
| return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationAccessError; |
| |
| if (!destination.isVirtual()) { |
| IFileStore destinationStore; |
| try { |
| destinationStore = EFS.getStore(destination.getLocationURI()); |
| } catch (CoreException exception) { |
| IDEWorkbenchPlugin.log(exception.getLocalizedMessage(), exception); |
| return NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_internalError, |
| exception.getLocalizedMessage()); |
| } |
| for (IFileStore fileStore : sourceStores) { |
| IFileStore parentFileStore = fileStore.getParent(); |
| |
| if (fileStore != null) { |
| if (destinationStore.equals(fileStore) |
| || (parentFileStore != null && destinationStore |
| .equals(parentFileStore))) { |
| return NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_importSameSourceAndDest, |
| fileStore.getName()); |
| } |
| // work around bug 16202. replacement for |
| // sourcePath.isPrefixOf(destinationPath) |
| if (fileStore.isParentOf(destinationStore)) { |
| return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationDescendentError; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Check if the destination is valid for the given source resource. |
| * |
| * @param destination |
| * destination container of the operation |
| * @param source |
| * source resource |
| * @return String error message or null if the destination is valid |
| */ |
| private String validateLinkedResource(IContainer destination, |
| IResource source) { |
| if ((source.isLinked() == false) || source.isVirtual()) { |
| return null; |
| } |
| IWorkspace workspace = destination.getWorkspace(); |
| IResource linkHandle = createLinkedResourceHandle(destination, source); |
| IStatus locationStatus = workspace.validateLinkLocationURI(linkHandle, |
| source.getRawLocationURI()); |
| |
| if (locationStatus.getSeverity() == IStatus.ERROR) { |
| return locationStatus.getMessage(); |
| } |
| IPath sourceLocation = source.getLocation(); |
| if (source.getProject().equals(destination.getProject()) == false |
| && source.getType() == IResource.FOLDER |
| && sourceLocation != null) { |
| // prevent merging linked folders that point to the same |
| // file system folder |
| try { |
| for (IResource resource : destination.members()) { |
| if (sourceLocation.equals(resource.getLocation()) && source.getName().equals(resource.getName())) { |
| return NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_sameSourceAndDest, |
| source.getName()); |
| } |
| } |
| } catch (CoreException exception) { |
| displayError(NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_internalError, |
| exception.getMessage())); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns whether moving all of the given source resources to the given |
| * destination container could be done without causing name collisions. |
| * |
| * @param destination |
| * the destination container |
| * @param sourceResources |
| * the list of resources |
| * @return <code>true</code> if there would be no name collisions, and |
| * <code>false</code> if there would |
| */ |
| private IResource[] validateNoNameCollisions(IContainer destination, |
| IResource[] sourceResources) { |
| List copyItems = new ArrayList(); |
| IWorkspaceRoot workspaceRoot = destination.getWorkspace().getRoot(); |
| int overwrite = IDialogConstants.NO_ID; |
| |
| // Check to see if we would be overwriting a parent folder. |
| // Cancel entire copy operation if we do. |
| for (final IResource resource : sourceResources) { |
| final IPath destinationPath = destination.getFullPath().append( |
| resource.getName()); |
| final IPath sourcePath = resource.getFullPath(); |
| |
| IResource newResource = workspaceRoot.findMember(destinationPath); |
| if (newResource != null && destinationPath.isPrefixOf(sourcePath)) { |
| displayError(NLS |
| .bind( |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteProblem, |
| destinationPath, sourcePath)); |
| |
| canceled = true; |
| return null; |
| } |
| } |
| // Check for overwrite conflicts |
| for (final IResource resource : sourceResources) { |
| final IPath destinationPath = destination.getFullPath().append( |
| resource.getName()); |
| |
| IResource newResource = workspaceRoot.findMember(destinationPath); |
| if (newResource != null) { |
| if (overwrite != IDialogConstants.YES_TO_ALL_ID |
| || (newResource.getType() == IResource.FOLDER && homogenousResources( |
| resource, destination) == false)) { |
| overwrite = checkOverwrite(resource, newResource); |
| } |
| if (overwrite == IDialogConstants.YES_ID |
| || overwrite == IDialogConstants.YES_TO_ALL_ID) { |
| copyItems.add(resource); |
| } else if (overwrite == IDialogConstants.CANCEL_ID) { |
| canceled = true; |
| return null; |
| } |
| } else { |
| copyItems.add(resource); |
| } |
| } |
| return (IResource[]) copyItems.toArray(new IResource[copyItems.size()]); |
| } |
| |
| private void copyResources(final IResource[] resources, final IPath destinationPath, |
| final IResource[][] copiedResources, IProgressMonitor mon) { |
| IResource[] copyResources = resources; |
| |
| // Fix for bug 31116. Do not provide a task name when |
| // creating the task. |
| SubMonitor subMonitor = SubMonitor.convert(mon, 100); |
| subMonitor.setTaskName(getOperationTitle()); |
| subMonitor.worked(10); // show some initial progress |
| |
| // Checks only required if this is an exisiting container path. |
| boolean copyWithAutoRename = false; |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| if (root.exists(destinationPath)) { |
| IContainer container = (IContainer) root |
| .findMember(destinationPath); |
| // If we're copying to the source container then perform |
| // auto-renames on all resources to avoid name collisions. |
| if (isDestinationSameAsSource(copyResources, container) |
| && canPerformAutoRename()) { |
| copyWithAutoRename = true; |
| } else { |
| // If no auto-renaming will be happening, check for |
| // potential name collisions at the target resource |
| copyResources = validateNoNameCollisions(container, |
| copyResources); |
| if (copyResources == null) { |
| if (canceled) { |
| return; |
| } |
| displayError(IDEWorkbenchMessages.CopyFilesAndFoldersOperation_nameCollision); |
| return; |
| } |
| if (validateEdit(container, copyResources) == false) { |
| return; |
| } |
| } |
| } |
| |
| errorStatus = null; |
| if (copyResources.length > 0) { |
| if (copyWithAutoRename) { |
| performCopyWithAutoRename(copyResources, destinationPath, subMonitor.split(90)); |
| } else { |
| performCopy(copyResources, destinationPath, subMonitor.split(90)); |
| } |
| } |
| copiedResources[0] = copyResources; |
| } |
| |
| private void copyFileStores(final IFileStore[] stores, |
| final IPath destinationPath, IProgressMonitor monitor) { |
| // Checks only required if this is an exisiting container path. |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| if (root.exists(destinationPath)) { |
| IContainer container = (IContainer) root |
| .findMember(destinationPath); |
| |
| performFileImport(stores, container, monitor); |
| } |
| } |
| |
| /** |
| * Returns the model provider ids that are known to the client that |
| * instantiated this operation. |
| * |
| * @return the model provider ids that are known to the client that |
| * instantiated this operation. |
| * @since 3.2 |
| */ |
| public String[] getModelProviderIds() { |
| return modelProviderIds; |
| } |
| |
| /** |
| * Sets the model provider ids that are known to the client that |
| * instantiated this operation. Any potential side effects reported by these |
| * models during validation will be ignored. |
| * |
| * @param modelProviderIds |
| * the model providers known to the client who is using this |
| * operation. |
| * @since 3.2 |
| */ |
| public void setModelProviderIds(String[] modelProviderIds) { |
| this.modelProviderIds = modelProviderIds; |
| } |
| |
| /** |
| * Create virtual folders and links of the given files and folders to the |
| * destination. The current Thread is halted while the resources are copied |
| * using a WorkspaceModifyOperation. This method should be called from the |
| * UI Thread. |
| * |
| * @param fileNames |
| * names of the files to copy |
| * @param destination |
| * destination to which files will be copied |
| * @see WorkspaceModifyOperation |
| * @see Display#getThread() |
| * @see Thread#currentThread() |
| * @since 3.6 |
| */ |
| public void createVirtualFoldersAndLinks(final String[] fileNames, |
| IContainer destination) { |
| IFileStore[] stores = buildFileStores(fileNames); |
| if (stores == null) { |
| return; |
| } |
| |
| createVirtualFoldersAndLinks = true; |
| copyFileStores(destination, stores, true, null); |
| } |
| |
| /** |
| * Create links of the given files and folders to the destination. The |
| * current Thread is halted while the resources are copied using a |
| * WorkspaceModifyOperation. This method should be called from the UI |
| * Thread. |
| * |
| * @param fileNames |
| * names of the files to copy |
| * @param destination |
| * destination to which files will be copied |
| * @see WorkspaceModifyOperation |
| * @see Display#getThread() |
| * @see Thread#currentThread() |
| * @since 3.6 |
| */ |
| public void linkFiles(final String[] fileNames, IContainer destination) { |
| IFileStore[] stores = buildFileStores(fileNames); |
| if (stores == null) { |
| return; |
| } |
| |
| createLinks = true; |
| copyFileStores(destination, stores, true, null); |
| } |
| |
| /** |
| * Set whether or not virtual folders and links will be created under the destination |
| * container. |
| * |
| * @param value |
| * @since 3.6 |
| */ |
| public void setVirtualFolders(boolean value) { |
| createVirtualFoldersAndLinks = value; |
| } |
| |
| /** |
| * Set whether or not links will be created under the destination container. |
| * |
| * @param value |
| * @since 3.6 |
| */ |
| public void setCreateLinks(boolean value) { |
| createLinks = value; |
| } |
| |
| /** |
| * Set a variable relative to which the links are created |
| * |
| * @param variable |
| * @since 3.6 |
| */ |
| public void setRelativeVariable(String variable) { |
| relativeVariable = variable; |
| } |
| |
| /** |
| * Returns an AbstractWorkspaceOperation suitable for performing the move or |
| * copy operation that will move or copy the given resources to the given |
| * destination path. |
| * |
| * @param resources |
| * the resources to be moved or copied |
| * @param destinationPath |
| * the destination path to which the resources should be moved |
| * @return the operation that should be used to perform the move or cop |
| * @since 3.3 |
| */ |
| protected AbstractWorkspaceOperation getUndoableCopyOrMoveOperation( |
| IResource[] resources, IPath destinationPath) { |
| return new CopyResourcesOperation(resources, destinationPath, |
| IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyTitle); |
| |
| } |
| } |