com.aptana.ide.ui.io.actions.CopyFilesOperation.java Source code

Java tutorial

Introduction

Here is the source code for com.aptana.ide.ui.io.actions.CopyFilesOperation.java

Source

/**
 * Aptana Studio
 * Copyright (c) 2005-2012 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions).
 * Please see the license.html included with this distribution for details.
 * Any modifications to this file must keep this entire header intact.
 */
// $codepro.audit.disable questionableAssignment

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.IDialogConstants;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Shell;

import com.aptana.core.io.efs.SyncUtils;
import com.aptana.core.io.vfs.IExtendedFileStore;
import com.aptana.core.logging.IdeLog;
import com.aptana.ide.core.io.preferences.CloakingUtils;
import com.aptana.ide.ui.io.IOUIPlugin;
import com.aptana.ide.ui.io.Utils;
import com.aptana.ui.util.UIUtils;

/**
 * @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;

    private static enum OverwriteStatus {
        YES, YES_TO_ALL, NO, CANCEL
    }

    private OverwriteStatus overwriteStatus;

    /**
     * Constructor.
     * 
     * @param shell
     *            the active shell
     */
    public CopyFilesOperation(Shell shell) {
        if (shell == null) {
            fShell = UIUtils.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();
        }

        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 IAdaptable objects
     * @param sourceRoot
     *            the file store representing the root of source connection
     * @param destinationRoot
     *            the file store representing the root of target connection
     * @param listener
     *            an optional job listener
     */
    public void copyFiles(IAdaptable[] sources, IFileStore sourceRoot, IFileStore destinationRoot,
            IJobChangeListener listener) {
        IFileStore[] fileStores = new IFileStore[sources.length];
        for (int i = 0; i < fileStores.length; ++i) {
            fileStores[i] = Utils.getFileStore(sources[i]);
        }
        copyFiles(fileStores, sourceRoot, destinationRoot, listener);
    }

    /**
     * Copies an array of files from the source to the destination.
     * 
     * @param sources
     *            the array of filenames
     * @param sourceRoot
     *            the file store representing the root of source connection
     * @param destinationRoot
     *            the file store representing the root of target connection
     * @param listener
     *            an optional job listener
     */
    public void copyFiles(String[] filenames, IFileStore sourceRoot, IFileStore destinationRoot,
            IJobChangeListener listener) {
        copyFiles(getFileStores(filenames), sourceRoot, destinationRoot, listener);
    }

    /**
     * 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();
        }

        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()));

        if (destinationStore.equals(sourceStore)) {
            destinationStore = getNewNameFor(destinationStore);
            if (destinationStore == null) {
                return false;
            }
        }
        try {
            IFileStore[] childStores = Utils.isDirectory(sourceStore) ? sourceStore.childStores(EFS.NONE, monitor)
                    : new IFileStore[0];
            if (Utils.exists(destinationStore) && sourceStore.getName().equals(destinationStore.getName())) {
                // a name conflict; ask to overwrite
                if (overwriteStatus != OverwriteStatus.YES_TO_ALL) {
                    final IFileStore dStore = destinationStore;
                    final IFileStore sStore = sourceStore;
                    fShell.getDisplay().syncExec(new Runnable() {

                        public void run() {
                            MessageDialog dialog = new MessageDialog(fShell,
                                    Messages.CopyFilesOperation_OverwriteTitle, null,
                                    MessageFormat.format(Messages.CopyFilesOperation_OverwriteWarning,
                                            dStore.toString(), sStore.toString()),
                                    MessageDialog.CONFIRM,
                                    new String[] { IDialogConstants.YES_LABEL, IDialogConstants.YES_TO_ALL_LABEL,
                                            IDialogConstants.NO_LABEL, IDialogConstants.CANCEL_LABEL },
                                    0);
                            int retCode = dialog.open();
                            switch (retCode) {
                            case 0: // Yes
                                overwriteStatus = OverwriteStatus.YES;
                                break;
                            case 1: // Yes to All
                                overwriteStatus = OverwriteStatus.YES_TO_ALL;
                                break;
                            case 2: // No
                                overwriteStatus = OverwriteStatus.NO;
                                break;
                            default:
                                overwriteStatus = OverwriteStatus.CANCEL;
                            }
                        }
                    });
                    switch (overwriteStatus) {
                    case CANCEL:
                        monitor.setCanceled(true);
                        // let it fall through since it would return false as well
                    case NO:
                        return false;
                    }
                }
            }
            SyncUtils.copy(sourceStore, null, destinationStore, EFS.NONE, monitor);

            // copy the children recursively
            IFileStore destChildStore;
            for (IFileStore childStore : childStores) {
                destChildStore = destinationStore.getChild(childStore.getName());
                copyFile(childStore, destChildStore, monitor);
            }
        } catch (CoreException e) {
            IdeLog.logError(IOUIPlugin.getDefault(), MessageFormat
                    .format(Messages.CopyFilesOperation_ERR_FailedToCopy, sourceStore, destinationStore), e);
            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();
    }

    private void copyFiles(final IFileStore[] sources, final IFileStore sourceRoot,
            final IFileStore destinationRoot, IJobChangeListener listener) {
        Job job = new Job(Messages.CopyFilesOperation_CopyJob_Title) {

            @Override
            protected IStatus run(IProgressMonitor monitor) {
                return copyFiles(sources, sourceRoot, destinationRoot, 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$
        final String filename = originalFile.getName();
        final boolean isRemote = (originalFile instanceof IExtendedFileStore);

        fShell.getDisplay().syncExec(new Runnable() {

            public void run() {
                IInputValidator validator = new IInputValidator() {
                    public String isValid(String string) {
                        if (filename.equals(string)) {
                            return Messages.CopyFilesOperation_ERR_NameConflict;
                        }
                        if (!isRemote) {
                            int type = Utils.isDirectory(originalFile) ? IResource.FOLDER : IResource.FILE;
                            IStatus status = ResourcesPlugin.getWorkspace().validateName(string, type);
                            if (!status.isOK()) {
                                return status.getMessage();
                            }
                            if (Utils.exists(parent.getChild(string))) {
                                return Messages.CopyFilesOperation_ERR_NameExists;
                            }
                        }
                        return null;
                    }
                };

                InputDialog dialog = new InputDialog(fShell, Messages.CopyFilesOperation_NameConflictDialog_Title,
                        MessageFormat.format(Messages.CopyFilesOperation_NameConflictDialog_Message, filename),
                        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();
        boolean isRemote = (originalFile instanceof IExtendedFileStore);

        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 (isRemote || !Utils.exists(parent.getChild(newName))) {
                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) {
        IResource resource = (IResource) destination.getAdapter(IResource.class);
        if (resource != null && !resource.isAccessible()) {
            return Messages.CopyFilesOperation_DestinationNotAccessible;
        }
        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, IExtendedFileStore.EXISTENCE);
        if (store != null && info != null && !info.isDirectory()) {
            store = store.getParent();
        }
        return store;
    }
}