com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil.java

Source

/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.intellij.refactoring.move.moveFilesOrDirectories;

import com.intellij.ide.util.DirectoryChooserUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.RefactoringSettings;
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler;
import com.intellij.refactoring.move.MoveCallback;
import com.intellij.refactoring.move.MoveHandler;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class MoveFilesOrDirectoriesUtil {
    private static final Logger LOG = Logger
            .getInstance("#com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil");

    private MoveFilesOrDirectoriesUtil() {
    }

    /**
     * Moves the specified directory to the specified parent directory. Does not process non-code usages!
     *
     * @param dir          the directory to move.
     * @param newParentDir the directory to move <code>dir</code> into.
     * @throws IncorrectOperationException if the modification is not supported or not possible for some reason.
     */
    public static void doMoveDirectory(final PsiDirectory aDirectory, final PsiDirectory destDirectory)
            throws IncorrectOperationException {
        PsiManager manager = aDirectory.getManager();
        // do actual move
        checkMove(aDirectory, destDirectory);

        try {
            aDirectory.getVirtualFile().move(manager, destDirectory.getVirtualFile());
        } catch (IOException e) {
            throw new IncorrectOperationException(e.toString(), e);
        }
    }

    /**
     * Moves the specified file to the specified directory. Does not process non-code usages!
     *
     * @param file         the file to move.
     * @param newDirectory the directory to move the file into.
     * @throws IncorrectOperationException if the modification is not supported or not possible for some reason.
     */
    public static void doMoveFile(final PsiFile file, final PsiDirectory newDirectory)
            throws IncorrectOperationException {
        PsiManager manager = file.getManager();
        // the class is already there, this is true when multiple classes are defined in the same file
        if (!newDirectory.equals(file.getContainingDirectory())) {
            // do actual move
            checkMove(file, newDirectory);

            try {
                final VirtualFile virtualFile = file.getVirtualFile();
                LOG.assertTrue(virtualFile != null, file);
                virtualFile.move(manager, newDirectory.getVirtualFile());
            } catch (IOException e) {
                throw new IncorrectOperationException(e.toString(), e);
            }
        }
    }

    /**
     * @param elements should contain PsiDirectories or PsiFiles only
     */
    public static void doMove(final Project project, final PsiElement[] elements, final PsiElement[] targetElement,
            final MoveCallback moveCallback) {
        doMove(project, elements, targetElement, moveCallback, null);
    }

    /**
     * @param elements should contain PsiDirectories or PsiFiles only if adjustElements == null
     */
    public static void doMove(final Project project, final PsiElement[] elements, final PsiElement[] targetElement,
            final MoveCallback moveCallback, final Function<PsiElement[], PsiElement[]> adjustElements) {
        if (adjustElements == null) {
            for (PsiElement element : elements) {
                if (!(element instanceof PsiFile) && !(element instanceof PsiDirectory)) {
                    throw new IllegalArgumentException("unexpected element type: " + element);
                }
            }
        }

        final PsiDirectory targetDirectory = resolveToDirectory(project, targetElement[0]);
        if (targetElement[0] != null && targetDirectory == null)
            return;

        final PsiElement[] newElements = adjustElements != null ? adjustElements.fun(elements) : elements;

        final PsiDirectory initialTargetDirectory = getInitialTargetDirectory(targetDirectory, elements);

        final MoveFilesOrDirectoriesDialog.Callback doRun = new MoveFilesOrDirectoriesDialog.Callback() {
            @Override
            public void run(final MoveFilesOrDirectoriesDialog moveDialog) {
                CommandProcessor.getInstance().executeCommand(project, new Runnable() {
                    @Override
                    public void run() {
                        final PsiDirectory targetDirectory = moveDialog != null ? moveDialog.getTargetDirectory()
                                : initialTargetDirectory;

                        LOG.assertTrue(targetDirectory != null);
                        targetElement[0] = targetDirectory;

                        PsiManager manager = PsiManager.getInstance(project);
                        try {
                            final int[] choice = elements.length > 1 || elements[0] instanceof PsiDirectory
                                    ? new int[] { -1 }
                                    : null;
                            final List<PsiElement> els = new ArrayList<PsiElement>();
                            for (int i = 0, newElementsLength = newElements.length; i < newElementsLength; i++) {
                                final PsiElement psiElement = newElements[i];
                                if (psiElement instanceof PsiFile) {
                                    final PsiFile file = (PsiFile) psiElement;
                                    final boolean fileExist = ApplicationManager.getApplication()
                                            .runWriteAction(new Computable<Boolean>() {
                                                @Override
                                                public Boolean compute() {
                                                    return CopyFilesOrDirectoriesHandler.checkFileExist(
                                                            targetDirectory, choice, file, file.getName(), "Move");
                                                }
                                            });
                                    if (fileExist)
                                        continue;
                                }
                                checkMove(psiElement, targetDirectory);
                                els.add(psiElement);
                            }
                            final Runnable callback = new Runnable() {
                                @Override
                                public void run() {
                                    if (moveDialog != null)
                                        moveDialog.close(DialogWrapper.CANCEL_EXIT_CODE);
                                }
                            };
                            if (els.isEmpty()) {
                                callback.run();
                                return;
                            }
                            new MoveFilesOrDirectoriesProcessor(project, els.toArray(new PsiElement[els.size()]),
                                    targetDirectory,
                                    RefactoringSettings.getInstance().MOVE_SEARCH_FOR_REFERENCES_FOR_FILE, false,
                                    false, moveCallback, callback).run();
                        } catch (IncorrectOperationException e) {
                            CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"),
                                    e.getMessage(), "refactoring.moveFile", project);
                        }
                    }
                }, MoveHandler.REFACTORING_NAME, null);
            }
        };

        if (ApplicationManager.getApplication().isUnitTestMode()) {
            doRun.run(null);
        } else {
            final MoveFilesOrDirectoriesDialog moveDialog = new MoveFilesOrDirectoriesDialog(project, doRun);
            moveDialog.setData(newElements, initialTargetDirectory, "refactoring.moveFile");
            moveDialog.show();
        }
    }

    @Nullable
    public static PsiDirectory resolveToDirectory(final Project project, final PsiElement element) {
        if (!(element instanceof PsiDirectoryContainer)) {
            return (PsiDirectory) element;
        }

        PsiDirectory[] directories = ((PsiDirectoryContainer) element).getDirectories();
        switch (directories.length) {
        case 0:
            return null;
        case 1:
            return directories[0];
        default:
            return DirectoryChooserUtil.chooseDirectory(directories, directories[0], project,
                    new HashMap<PsiDirectory, String>());
        }

    }

    @Nullable
    private static PsiDirectory getCommonDirectory(PsiElement[] movedElements) {
        PsiDirectory commonDirectory = null;

        for (PsiElement movedElement : movedElements) {
            final PsiDirectory containingDirectory;
            if (movedElement instanceof PsiDirectory) {
                containingDirectory = ((PsiDirectory) movedElement).getParentDirectory();
            } else {
                final PsiFile containingFile = movedElement.getContainingFile();
                containingDirectory = containingFile == null ? null : containingFile.getContainingDirectory();
            }

            if (containingDirectory != null) {
                if (commonDirectory == null) {
                    commonDirectory = containingDirectory;
                } else {
                    if (commonDirectory != containingDirectory) {
                        return null;
                    }
                }
            }
        }
        return commonDirectory;
    }

    @Nullable
    public static PsiDirectory getInitialTargetDirectory(PsiDirectory initialTargetElement,
            final PsiElement[] movedElements) {
        PsiDirectory initialTargetDirectory = initialTargetElement;
        if (initialTargetDirectory == null) {
            if (movedElements != null) {
                final PsiDirectory commonDirectory = getCommonDirectory(movedElements);
                if (commonDirectory != null) {
                    initialTargetDirectory = commonDirectory;
                } else {
                    initialTargetDirectory = getContainerDirectory(movedElements[0]);
                }
            }
        }
        return initialTargetDirectory;
    }

    @Nullable
    private static PsiDirectory getContainerDirectory(final PsiElement psiElement) {
        if (psiElement instanceof PsiDirectory) {
            return (PsiDirectory) psiElement;
        } else if (psiElement != null) {
            return psiElement.getContainingFile().getContainingDirectory();
        } else {
            return null;
        }
    }

    /**
     * Checks if it is possible to move the specified PSI element under the specified container,
     * and throws an exception if the move is not possible. Does not actually modify anything.
     *
     * @param element      the element to check the move possibility.
     * @param newContainer the target container element to move into.
     * @throws IncorrectOperationException if the modification is not supported or not possible for some reason.
     */
    public static void checkMove(@NotNull PsiElement element, @NotNull PsiElement newContainer)
            throws IncorrectOperationException {
        if (element instanceof PsiDirectoryContainer) {
            PsiDirectory[] dirs = ((PsiDirectoryContainer) element).getDirectories();
            if (dirs.length == 0) {
                throw new IncorrectOperationException();
            } else if (dirs.length > 1) {
                throw new IncorrectOperationException(
                        "Moving of packages represented by more than one physical directory is not supported.");
            }
            checkMove(dirs[0], newContainer);
            return;
        }

        //element.checkDelete(); //move != delete + add
        newContainer.checkAdd(element);
        checkIfMoveIntoSelf(element, newContainer);
    }

    public static void checkIfMoveIntoSelf(PsiElement element, PsiElement newContainer)
            throws IncorrectOperationException {
        PsiElement container = newContainer;
        while (container != null) {
            if (container == element) {
                if (element instanceof PsiDirectory) {
                    if (element == newContainer) {
                        throw new IncorrectOperationException("Cannot place directory into itself.");
                    } else {
                        throw new IncorrectOperationException("Cannot place directory into its subdirectory.");
                    }
                } else {
                    throw new IncorrectOperationException("Element is not directory: " + element);
                }
            }
            container = container.getParent();
        }
    }
}