/** * This file Copyright (c) 2005-2010 Aptana, Inc. This program is * dual-licensed under both the Aptana Public License and the GNU General * Public license. You may elect to use one or the other of these licenses. * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by whichever of * the GPL or APL you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or modify this * program under the terms of the GNU General Public License, * Version 3, as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain other free and open source software ("FOSS") code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * 2. For the Aptana Public License (APL), this program and the * accompanying materials are made available under the terms of the APL * v1.0 which accompanies this distribution, and is available at * http://www.aptana.com/legal/apl/. * * You may view the GPL, Aptana's exception and additional terms, and the * APL in the file titled license.html at the root of the corresponding * plugin containing this source file. * * Any modifications to this file must keep this entire header intact. */ package com.aptana.ide.ui.io.actions; import java.text.MessageFormat; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.IJobChangeListener; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.IInputValidator; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.window.Window; import org.eclipse.swt.widgets.Shell; import com.aptana.ide.core.FileUtils; import com.aptana.ide.core.IdeLog; import com.aptana.ide.core.io.preferences.CloakingUtils; import com.aptana.ide.core.ui.CoreUIUtils; import com.aptana.ide.core.ui.DialogUtils; import com.aptana.ide.core.ui.syncing.SyncingConsole; import com.aptana.ide.ui.io.IOUIPlugin; import com.aptana.ide.ui.io.internal.Utils; import com.aptana.ide.ui.io.preferences.IPreferenceConstants; /** * @author Michael Xia (mxia@aptana.com) */ public class CopyFilesOperation { /** * The parent shell used to show any dialogs */ private Shell fShell; /** * Flag to indicate if the operation has been canceled by the user */ private boolean fCancelled; /** * Overwrite-all flag */ private boolean fAlwaysOverwrite; /** * Constructor. * * @param shell * the active shell */ public CopyFilesOperation(Shell shell) { if (shell == null) { fShell = CoreUIUtils.getActiveShell(); } else { fShell = shell; } } /** * Copies an array of sources to the destination location. * * @param sources * the array of IAdaptable objects * @param destination * the destination file store * @param listener * an optional job listener */ public void copyFiles(IAdaptable[] sources, IFileStore destination, IJobChangeListener listener) { IFileStore[] fileStores = new IFileStore[sources.length]; for (int i = 0; i < fileStores.length; ++i) { fileStores[i] = Utils.getFileStore(sources[i]); } copyFiles(fileStores, destination, listener); } /** * Copies an array of sources to the destination location. * * @param sources * the array of filenames * @param destination * the destination file store * @param listener * an optional job listener */ public void copyFiles(String[] filenames, IFileStore destination, IJobChangeListener listener) { copyFiles(getFileStores(filenames), destination, listener); } /** * Copies an array of sources to the destination location. * * @param sources * the array of source file stores * @param destination * the file store representing the destination folder * @param monitor * an optional progress monitor */ public IStatus copyFiles(IFileStore[] sources, IFileStore destination, IProgressMonitor monitor) { if (monitor == null) { monitor = new NullProgressMonitor(); } fShell.getDisplay().syncExec(new Runnable() { public void run() { int retCode = DialogUtils.openIgnoreMessageDialogConfirm(fShell, Messages.CopyFilesOperation_OverwriteTitle, Messages.CopyFilesOperation_OverwriteWarning, IOUIPlugin.getDefault() .getPreferenceStore(), IPreferenceConstants.COPY_OVERWRITE); fAlwaysOverwrite = (retCode == Window.OK); } }); if (!fAlwaysOverwrite) { return Status.CANCEL_STATUS; } int successCount = 0; for (IFileStore source : sources) { if (copyFile(source, destination.getChild(source.getName()), monitor)) { successCount++; } if (fCancelled || monitor.isCanceled()) { return Status.CANCEL_STATUS; } } return new Status(IStatus.OK, IOUIPlugin.PLUGIN_ID, successCount, Messages.CopyFilesOperation_Status_OK, null); } /** * Copies an array of files from the source to the destination. * * @param sources * the array of source file stores * @param sourceRoot * the file store representing the root of source connection * @param destinationRoot * the file store representing the root of target connection * @param monitor * an optional progress monitor */ public IStatus copyFiles(IFileStore[] sources, IFileStore sourceRoot, IFileStore destinationRoot, IProgressMonitor monitor) { if (monitor == null) { monitor = new NullProgressMonitor(); } fShell.getDisplay().syncExec(new Runnable() { public void run() { int retCode = DialogUtils.openIgnoreMessageDialogConfirm(fShell, Messages.CopyFilesOperation_OverwriteTitle, Messages.CopyFilesOperation_OverwriteWarning, IOUIPlugin.getDefault() .getPreferenceStore(), IPreferenceConstants.COPY_OVERWRITE); fAlwaysOverwrite = (retCode == Window.OK); } }); if (!fAlwaysOverwrite) { return Status.CANCEL_STATUS; } int successCount = 0; for (IFileStore source : sources) { if (copyFile(source, sourceRoot, destinationRoot, monitor)) { successCount++; } if (fCancelled || monitor.isCanceled()) { return Status.CANCEL_STATUS; } } return new Status(IStatus.OK, IOUIPlugin.PLUGIN_ID, successCount, Messages.CopyFilesOperation_Status_OK, null); } /** * Checks if there is structural conflict for transferring the sources to * the destination. * * @param destination * the destination adaptable * @param sources * the array of source adaptables * @return a descriptive error message if the validation fails, and null * otherwise */ public static String validateDestination(IAdaptable destination, IAdaptable[] sources) { IFileStore[] sourceStores = new IFileStore[sources.length]; for (int i = 0; i < sourceStores.length; ++i) { sourceStores[i] = Utils.getFileStore(sources[i]); } return validateDestination(destination, sourceStores); } /** * Checks if there is structural conflict for transferring the sources to * the destination. * * @param destination * the destination adaptable * @param sourceNames * the array of source filenames * @return a descriptive error message if the validation fails, and null * otherwise */ public static String validateDestination(IAdaptable destination, String[] sourceNames) { return validateDestination(destination, getFileStores(sourceNames)); } /** * @param sourceStore * the file to be copied * @param destinationStore * the destination location * @param monitor * the progress monitor * @return true if the file is successfully copied, false if the operation * did not go through for any reason */ protected boolean copyFile(IFileStore sourceStore, IFileStore destinationStore, IProgressMonitor monitor) { if (sourceStore == null || CloakingUtils.isFileCloaked(sourceStore)) { return false; } boolean success = true; monitor.subTask(MessageFormat.format(Messages.CopyFilesOperation_Copy_Subtask, sourceStore .getName(), destinationStore.getName())); SyncingConsole.println(FileUtils.NEW_LINE + MessageFormat.format(Messages.CopyFilesOperation_Copy_Subtask, sourceStore .toURI().toString(), destinationStore.toURI().toString())); if (destinationStore.equals(sourceStore)) { destinationStore = getNewNameFor(destinationStore); if (destinationStore == null) { SyncingConsole.println(". Failed."); return false; } } try { sourceStore.copy(destinationStore, EFS.OVERWRITE, monitor); SyncingConsole.println(". Success."); } catch (CoreException e) { IdeLog .logError(IOUIPlugin.getDefault(), MessageFormat.format( Messages.CopyFilesOperation_ERR_FailedToCopy, sourceStore, destinationStore), e); SyncingConsole.println(". Failure."); success = false; } return success; } /** * @param sourceStore * the file to be copied * @param sourceRoot * the source root * @param destinationRoot * the destination root * @param monitor * the progress monitor * @return true if the file is successfully copied, false if the operation * did not go through for any reason */ protected boolean copyFile(IFileStore sourceStore, IFileStore sourceRoot, IFileStore destinationRoot, IProgressMonitor monitor) { if (sourceStore == null || CloakingUtils.isFileCloaked(sourceStore)) { return false; } boolean success = true; IFileStore[] sourceStores = null, targetStores = null; try { if (sourceStore.equals(sourceRoot)) { // copying the whole source sourceStores = sourceRoot.childStores(EFS.NONE, monitor); targetStores = new IFileStore[sourceStores.length]; for (int i = 0; i < targetStores.length; ++i) { targetStores[i] = destinationRoot.getChild(sourceStores[i].getName()); } } else if (sourceRoot.isParentOf(sourceStore)) { // finds the relative path of the file to be copied and maps to // the destination target sourceStores = new IFileStore[1]; sourceStores[0] = sourceStore; targetStores = new IFileStore[1]; String sourceRootPath = sourceRoot.toString(); String sourcePath = sourceStore.toString(); int index = sourcePath.indexOf(sourceRootPath); if (index > -1) { String relativePath = sourcePath.substring(index + sourceRootPath.length()); targetStores[0] = destinationRoot.getFileStore(new Path(relativePath)); // makes sure the parent folder is created on the // destination side IFileStore parent = getFolderStore(targetStores[0]); if (parent != targetStores[0]) { parent.mkdir(EFS.NONE, monitor); } } } if (sourceStores == null) { // the file to be copied is not a child of the source root; // cannot copy success = false; sourceStores = new IFileStore[0]; targetStores = new IFileStore[0]; } for (int i = 0; i < sourceStores.length; ++i) { success = copyFile(sourceStores[i], targetStores[i], monitor) && success; } } catch (CoreException e) { IdeLog.logError(IOUIPlugin.getDefault(), MessageFormat.format( Messages.CopyFilesOperation_ERR_FailedToCopyToDest, sourceStore, destinationRoot), e); success = false; } return success; } private void copyFiles(final IFileStore[] sources, final IFileStore destination, IJobChangeListener listener) { Job job = new Job(Messages.CopyFilesOperation_CopyJob_Title) { @Override protected IStatus run(IProgressMonitor monitor) { return copyFiles(sources, destination, monitor); } public boolean belongsTo(Object family) { if (Messages.CopyFilesOperation_CopyJob_Title.equals(family)) { return true; } return super.belongsTo(family); } }; if (listener != null) { job.addJobChangeListener(listener); } job.setUser(true); job.schedule(); } /** * Returns a new name for a copy of the file. * * @param originalFile * the file store * @return the new file store for the copy, or <code>null</code> if the file should not be copied */ private IFileStore getNewNameFor(final IFileStore originalFile) { final IFileStore parent = originalFile.getParent(); final String returnValue[] = { "" }; //$NON-NLS-1$ fShell.getDisplay().syncExec(new Runnable() { public void run() { IInputValidator validator = new IInputValidator() { public String isValid(String string) { if (originalFile.getName().equals(string)) { return Messages.CopyFilesOperation_ERR_NameConflict; } int type = Utils.getFileInfo(originalFile).isDirectory() ? IResource.FOLDER : IResource.FILE; IStatus status = ResourcesPlugin.getWorkspace().validateName(string, type); if (!status.isOK()) { return status.getMessage(); } if (Utils.getFileInfo(parent.getChild(string)).exists()) { return Messages.CopyFilesOperation_ERR_NameExists; } return null; } }; InputDialog dialog = new InputDialog(fShell, Messages.CopyFilesOperation_NameConflictDialog_Title, MessageFormat.format(Messages.CopyFilesOperation_NameConflictDialog_Message, originalFile .getName()), getAutoNewNameFor(originalFile), validator); dialog.setBlockOnOpen(true); dialog.open(); if (dialog.getReturnCode() == Window.CANCEL) { returnValue[0] = null; } else { returnValue[0] = dialog.getValue(); } } }); if (returnValue[0] == null) { return null; } return parent.getChild(returnValue[0]); } private static String getAutoNewNameFor(IFileStore originalFile) { String name = originalFile.getName(); IFileStore parent = originalFile.getParent(); String newName; int counter = 1; while (true) { if (counter > 1) { newName = MessageFormat.format(Messages.CopyFilesOperation_DefaultNewName_WithCount, counter, name); } else { newName = MessageFormat.format(Messages.CopyFilesOperation_DefaultNewName, name); } if (!Utils.getFileInfo(parent.getChild(newName)).exists()) { return newName; } counter++; } } /** * Checks if there is structural conflict for transferring the sources to * the destination. * * @param destination * the destination adaptable * @param sourceStores * the array of source stores * @return a descriptive error message if the validation fails, and null * otherwise */ private static String validateDestination(IAdaptable destination, IFileStore[] sourceStores) { if (destination instanceof IResource && !((IResource) destination).isAccessible()) { return "The destination is not accessible"; } IFileStore destinationStore = getFolderStore(destination); IFileStore sourceParentStore; for (IFileStore sourceStore : sourceStores) { sourceParentStore = sourceStore.getParent(); if (destinationStore.equals(sourceStore) || (sourceParentStore != null && destinationStore.equals(sourceParentStore))) { return Messages.CopyFilesOperation_ERR_SourceInDestination; } if (sourceStore.isParentOf(destinationStore)) { return Messages.CopyFilesOperation_ERR_DestinationInSource; } } return null; } /** * @param filename * the filename * @return the corresponding file store, or null if it could not be found */ private static IFileStore getFileStore(String filename) { try { return EFS.getStore((new Path(filename).toFile().toURI())); } catch (CoreException e) { } return null; } /** * @param filenames * an array of filenames * @return the array of corresponding file stores */ private static IFileStore[] getFileStores(String[] filenames) { IFileStore[] fileStores = new IFileStore[filenames.length]; for (int i = 0; i < fileStores.length; ++i) { fileStores[i] = getFileStore(filenames[i]); } return fileStores; } /** * Gets the folder the file belongs in. If the file is a directory, returns * itself. * * @param adaptable * an IAdaptable that could adapt to an IFileStore * @return the folder file store */ private static IFileStore getFolderStore(IAdaptable adaptable) { IFileStore store = Utils.getFileStore(adaptable); IFileInfo info = Utils.getFileInfo(adaptable); if (store != null && info != null && !info.isDirectory()) { store = store.getParent(); } return store; } }