org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog.java

Source

/*
 * Copyright 2010-2015 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 org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui;

import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.JavaProjectRootsUtil;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.util.Pass;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.refactoring.JavaRefactoringSettings;
import com.intellij.refactoring.MoveDestination;
import com.intellij.refactoring.PackageWrapper;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel;
import com.intellij.refactoring.classMembers.MemberInfoChange;
import com.intellij.refactoring.classMembers.MemberInfoChangeListener;
import com.intellij.refactoring.move.MoveCallback;
import com.intellij.refactoring.move.MoveHandler;
import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox;
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor;
import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo;
import com.intellij.refactoring.ui.RefactoringDialog;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.ui.ComboboxWithBrowseButton;
import com.intellij.ui.RecentsManager;
import com.intellij.ui.ReferenceEditorComboWithBrowseButton;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.text.UniqueNameGenerator;
import com.intellij.util.ui.UIUtil;
import kotlin.collections.ArraysKt;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.KotlinFileType;
import org.jetbrains.kotlin.idea.core.PackageUtilsKt;
import org.jetbrains.kotlin.idea.refactoring.JetRefactoringUtilKt;
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionPanel;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionTable;
import org.jetbrains.kotlin.idea.refactoring.move.MoveUtilsKt;
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*;
import org.jetbrains.kotlin.idea.refactoring.ui.KotlinFileChooserDialog;
import org.jetbrains.kotlin.idea.util.application.ApplicationUtilsKt;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.psi.KtNamedDeclaration;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.*;
import java.util.List;

public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
    private static final String RECENTS_KEY = "MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY";

    private static class MemberInfoModelImpl extends AbstractMemberInfoModel<KtNamedDeclaration, KotlinMemberInfo> {

    }

    private JCheckBox cbSearchInComments;
    private JCheckBox cbSearchTextOccurrences;
    private JPanel mainPanel;
    private ReferenceEditorComboWithBrowseButton classPackageChooser;
    private ComboboxWithBrowseButton destinationFolderCB;
    private JPanel targetPanel;
    private JRadioButton rbMoveToPackage;
    private JRadioButton rbMoveToFile;
    private TextFieldWithBrowseButton fileChooser;
    private JPanel memberInfoPanel;
    private JTextField tfFileNameInPackage;
    private JCheckBox cbSpecifyFileNameInPackage;
    private JCheckBox cbUpdatePackageDirective;
    private KotlinMemberSelectionTable memberTable;

    private final MoveCallback moveCallback;

    public MoveKotlinTopLevelDeclarationsDialog(@NotNull Project project,
            @NotNull Set<KtNamedDeclaration> elementsToMove, @Nullable String targetPackageName,
            @Nullable PsiDirectory targetDirectory, @Nullable KtFile targetFile, boolean moveToPackage,
            boolean searchInComments, boolean searchForTextOccurences, @Nullable MoveCallback moveCallback) {
        super(project, true);

        List<KtFile> sourceFiles = getSourceFiles(elementsToMove);

        this.moveCallback = moveCallback;

        init();

        setTitle(MoveHandler.REFACTORING_NAME);

        initSearchOptions(searchInComments, searchForTextOccurences);

        initPackageChooser(targetPackageName, targetDirectory, sourceFiles);

        initFileChooser(targetFile, elementsToMove, sourceFiles);

        initMoveToButtons(moveToPackage);

        initMemberInfo(elementsToMove, sourceFiles);

        updateControls();
    }

    private static List<KtFile> getSourceFiles(@NotNull Collection<KtNamedDeclaration> elementsToMove) {
        return CollectionsKt
                .distinct(CollectionsKt.map(elementsToMove, new Function1<KtNamedDeclaration, KtFile>() {
                    @Override
                    public KtFile invoke(KtNamedDeclaration declaration) {
                        return declaration.getContainingKtFile();
                    }
                }));
    }

    @NotNull
    private static PsiDirectory getSourceDirectory(@NotNull Collection<KtFile> sourceFiles) {
        return CollectionsKt.single(
                CollectionsKt.distinct(CollectionsKt.map(sourceFiles, new Function1<KtFile, PsiDirectory>() {
                    @Override
                    public PsiDirectory invoke(KtFile jetFile) {
                        return jetFile.getParent();
                    }
                })));
    }

    private static List<KtNamedDeclaration> getAllDeclarations(Collection<KtFile> sourceFiles) {
        return CollectionsKt
                .filterIsInstance(CollectionsKt.flatMap(sourceFiles, new Function1<KtFile, Iterable<?>>() {
                    @Override
                    public Iterable<?> invoke(KtFile jetFile) {
                        return jetFile.getDeclarations();
                    }
                }), KtNamedDeclaration.class);
    }

    private static boolean arePackagesAndDirectoryMatched(List<KtFile> sourceFiles) {
        for (KtFile sourceFile : sourceFiles) {
            if (!PackageUtilsKt.packageMatchesDirectory(sourceFile))
                return false;
        }
        return true;
    }

    private void initMemberInfo(@NotNull final Set<KtNamedDeclaration> elementsToMove,
            @NotNull List<KtFile> sourceFiles) {
        final List<KotlinMemberInfo> memberInfos = CollectionsKt.map(getAllDeclarations(sourceFiles),
                new Function1<KtNamedDeclaration, KotlinMemberInfo>() {
                    @Override
                    public KotlinMemberInfo invoke(KtNamedDeclaration declaration) {
                        KotlinMemberInfo memberInfo = new KotlinMemberInfo(declaration, false);
                        memberInfo.setChecked(elementsToMove.contains(declaration));
                        return memberInfo;
                    }
                });
        KotlinMemberSelectionPanel selectionPanel = new KotlinMemberSelectionPanel(getTitle(), memberInfos, null);
        memberTable = selectionPanel.getTable();
        MemberInfoModelImpl memberInfoModel = new MemberInfoModelImpl();
        memberInfoModel.memberInfoChanged(new MemberInfoChange<KtNamedDeclaration, KotlinMemberInfo>(memberInfos));
        selectionPanel.getTable().setMemberInfoModel(memberInfoModel);
        selectionPanel.getTable().addMemberInfoChangeListener(memberInfoModel);
        selectionPanel.getTable()
                .addMemberInfoChangeListener(new MemberInfoChangeListener<KtNamedDeclaration, KotlinMemberInfo>() {
                    private boolean shouldUpdateFileNameField(final Collection<KotlinMemberInfo> changedMembers) {
                        if (!tfFileNameInPackage.isEnabled())
                            return true;

                        Collection<KtNamedDeclaration> previousDeclarations = CollectionsKt
                                .filterNotNull(CollectionsKt.map(memberInfos,
                                        new Function1<KotlinMemberInfo, KtNamedDeclaration>() {
                                            @Override
                                            public KtNamedDeclaration invoke(KotlinMemberInfo info) {
                                                return changedMembers.contains(info) != info.isChecked()
                                                        ? info.getMember()
                                                        : null;
                                            }
                                        }));
                        String suggestedText = previousDeclarations.isEmpty() ? ""
                                : MoveUtilsKt.guessNewFileName(previousDeclarations);
                        return tfFileNameInPackage.getText().equals(suggestedText);
                    }

                    @Override
                    public void memberInfoChanged(MemberInfoChange<KtNamedDeclaration, KotlinMemberInfo> event) {
                        updatePackageDirectiveCheckBox();
                        updateFileNameInPackageField();
                        // Update file name field only if it user hasn't changed it to some non-default value
                        if (shouldUpdateFileNameField(event.getChangedMembers())) {
                            updateSuggestedFileName();
                        }
                    }
                });
        memberInfoPanel.add(selectionPanel, BorderLayout.CENTER);
    }

    private void updateSuggestedFileName() {
        tfFileNameInPackage.setText(MoveUtilsKt.guessNewFileName(getSelectedElementsToMove()));

    }

    private void updateFileNameInPackageField() {
        boolean movingSingleFileToPackage = isMoveToPackage()
                && getSourceFiles(getSelectedElementsToMove()).size() == 1;
        cbSpecifyFileNameInPackage.setEnabled(movingSingleFileToPackage);
        tfFileNameInPackage.setEnabled(movingSingleFileToPackage && cbSpecifyFileNameInPackage.isSelected());
    }

    private void initPackageChooser(String targetPackageName, PsiDirectory targetDirectory,
            List<KtFile> sourceFiles) {
        if (targetPackageName != null) {
            classPackageChooser.prependItem(targetPackageName);
        }

        ((DestinationFolderComboBox) destinationFolderCB).setData(myProject, targetDirectory, new Pass<String>() {
            @Override
            public void pass(String s) {
                setErrorText(s);
            }
        }, classPackageChooser.getChildComponent());

        cbSpecifyFileNameInPackage.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(@NotNull ActionEvent e) {
                updateFileNameInPackageField();
            }
        });

        cbUpdatePackageDirective.setSelected(arePackagesAndDirectoryMatched(sourceFiles));
    }

    private void initSearchOptions(boolean searchInComments, boolean searchForTextOccurences) {
        cbSearchInComments.setSelected(searchInComments);
        cbSearchTextOccurrences.setSelected(searchForTextOccurences);
    }

    private void initMoveToButtons(boolean moveToPackage) {
        if (moveToPackage) {
            rbMoveToPackage.setSelected(true);
        } else {
            rbMoveToFile.setSelected(true);
        }

        rbMoveToPackage.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(@NotNull ActionEvent e) {
                classPackageChooser.requestFocus();
                updateControls();
            }
        });

        rbMoveToFile.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(@NotNull ActionEvent e) {
                fileChooser.requestFocus();
                updateControls();
            }
        });
    }

    private void initFileChooser(@Nullable KtFile targetFile, @NotNull Set<KtNamedDeclaration> elementsToMove,
            @NotNull List<KtFile> sourceFiles) {
        final PsiDirectory sourceDir = sourceFiles.get(0).getParent();
        assert sourceDir != null : sourceFiles.get(0).getVirtualFile().getPath();

        fileChooser.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                KotlinFileChooserDialog dialog = new KotlinFileChooserDialog("Choose Containing File", myProject);

                File targetFile = new File(getTargetFilePath());
                PsiFile targetPsiFile = JetRefactoringUtilKt.toPsiFile(targetFile, myProject);
                if (targetPsiFile instanceof KtFile) {
                    dialog.select((KtFile) targetPsiFile);
                } else {
                    PsiDirectory targetDir = JetRefactoringUtilKt.toPsiDirectory(targetFile.getParentFile(),
                            myProject);
                    if (targetDir == null) {
                        targetDir = sourceDir;
                    }
                    dialog.selectDirectory(targetDir);
                }

                dialog.showDialog();
                KtFile selectedFile = dialog.isOK() ? dialog.getSelected() : null;
                if (selectedFile != null) {
                    fileChooser.setText(selectedFile.getVirtualFile().getPath());
                }
            }
        });

        String initialTargetPath = targetFile != null ? targetFile.getVirtualFile().getPath()
                : sourceFiles.get(0).getVirtualFile().getParent().getPath() + "/"
                        + MoveUtilsKt.guessNewFileName(elementsToMove);
        fileChooser.setText(initialTargetPath);
    }

    private void createUIComponents() {
        classPackageChooser = createPackageChooser();

        destinationFolderCB = new DestinationFolderComboBox() {
            @Override
            public String getTargetPackage() {
                return MoveKotlinTopLevelDeclarationsDialog.this.getTargetPackage();
            }
        };
    }

    private ReferenceEditorComboWithBrowseButton createPackageChooser() {
        ReferenceEditorComboWithBrowseButton packageChooser = new PackageNameReferenceEditorCombo("", myProject,
                RECENTS_KEY, RefactoringBundle.message("choose.destination.package"));
        Document document = packageChooser.getChildComponent().getDocument();
        document.addDocumentListener(new DocumentAdapter() {
            @Override
            public void documentChanged(DocumentEvent e) {
                validateButtons();
            }
        });

        return packageChooser;
    }

    private void updateControls() {
        boolean moveToPackage = isMoveToPackage();
        classPackageChooser.setEnabled(moveToPackage);
        updateFileNameInPackageField();
        fileChooser.setEnabled(!moveToPackage);
        updatePackageDirectiveCheckBox();
        UIUtil.setEnabled(targetPanel, moveToPackage && hasAnySourceRoots(), true);
        updateSuggestedFileName();
        validateButtons();
    }

    private boolean isFullFileMove() {
        Map<KtFile, List<KtNamedDeclaration>> fileToElements = CollectionsKt.groupBy(getSelectedElementsToMove(),
                new Function1<KtNamedDeclaration, KtFile>() {
                    @Override
                    public KtFile invoke(KtNamedDeclaration declaration) {
                        return declaration.getContainingKtFile();
                    }
                });
        for (Map.Entry<KtFile, List<KtNamedDeclaration>> entry : fileToElements.entrySet()) {
            if (entry.getKey().getDeclarations().size() != entry.getValue().size())
                return false;
        }
        return true;
    }

    private void updatePackageDirectiveCheckBox() {
        cbUpdatePackageDirective.setEnabled(isMoveToPackage() && isFullFileMove());
    }

    private boolean hasAnySourceRoots() {
        return !JavaProjectRootsUtil.getSuitableDestinationSourceRoots(myProject).isEmpty();
    }

    private void saveRefactoringSettings() {
        JavaRefactoringSettings refactoringSettings = JavaRefactoringSettings.getInstance();
        refactoringSettings.MOVE_SEARCH_IN_COMMENTS = isSearchInComments();
        refactoringSettings.MOVE_SEARCH_FOR_TEXT = isSearchInNonJavaFiles();
        refactoringSettings.MOVE_PREVIEW_USAGES = isPreviewUsages();
    }

    @Nullable
    private MoveDestination selectPackageBasedMoveDestination(boolean askIfDoesNotExist) {
        String packageName = getTargetPackage();

        RecentsManager.getInstance(myProject).registerRecentEntry(RECENTS_KEY, packageName);
        PackageWrapper targetPackage = new PackageWrapper(PsiManager.getInstance(myProject), packageName);
        if (!targetPackage.exists() && askIfDoesNotExist) {
            int ret = Messages.showYesNoDialog(myProject,
                    RefactoringBundle.message("package.does.not.exist", packageName),
                    RefactoringBundle.message("move.title"), Messages.getQuestionIcon());
            if (ret != Messages.YES)
                return null;
        }

        return ((DestinationFolderComboBox) destinationFolderCB).selectDirectory(targetPackage, false);
    }

    private boolean checkTargetFileName(String fileName) {
        if (FileTypeManager.getInstance().getFileTypeByFileName(fileName) == KotlinFileType.INSTANCE)
            return true;
        setErrorText("Can't move to non-Kotlin file");
        return false;
    }

    @NotNull
    private static List<PsiFile> getFilesExistingInTargetDir(@NotNull List<KtFile> sourceFiles,
            @Nullable String targetFileName, @Nullable final PsiDirectory targetDirectory) {
        if (targetDirectory == null)
            return Collections.emptyList();

        List<String> fileNames = targetFileName != null ? Collections.singletonList(targetFileName)
                : CollectionsKt.map(sourceFiles, new Function1<KtFile, String>() {
                    @Override
                    public String invoke(KtFile jetFile) {
                        return jetFile.getName();
                    }
                });

        return CollectionsKt.filterNotNull(CollectionsKt.map(fileNames, new Function1<String, PsiFile>() {
            @Override
            public PsiFile invoke(String s) {
                return targetDirectory.findFile(s);
            }
        }));
    }

    @Nullable
    private KotlinMoveTarget selectMoveTarget() {
        String message = verifyBeforeRun();
        if (message != null) {
            setErrorText(message);
            return null;
        }

        setErrorText(null);

        List<KtFile> sourceFiles = getSourceFiles(getSelectedElementsToMove());
        PsiDirectory sourceDirectory = getSourceDirectory(sourceFiles);

        if (isMoveToPackage()) {
            final MoveDestination moveDestination = selectPackageBasedMoveDestination(true);
            if (moveDestination == null)
                return null;

            final String targetFileName = sourceFiles.size() > 1 ? null : tfFileNameInPackage.getText();
            if (targetFileName != null && !checkTargetFileName(targetFileName))
                return null;

            PsiDirectory targetDirectory = moveDestination.getTargetIfExists(sourceDirectory);

            List<PsiFile> filesExistingInTargetDir = getFilesExistingInTargetDir(sourceFiles, targetFileName,
                    targetDirectory);
            if (!filesExistingInTargetDir.isEmpty()) {
                if (!CollectionsKt.intersect(sourceFiles, filesExistingInTargetDir).isEmpty()) {
                    setErrorText("Can't move to the original file(s)");
                    return null;
                }

                if (filesExistingInTargetDir.size() > 1) {
                    String filePathsToReport = StringUtil.join(filesExistingInTargetDir,
                            new Function<PsiFile, String>() {
                                @Override
                                public String fun(PsiFile file) {
                                    return file.getVirtualFile().getPath();
                                }
                            }, "\n");
                    Messages.showErrorDialog(myProject,
                            "Cannot perform refactoring since the following files already exist:\n\n"
                                    + filePathsToReport,
                            RefactoringBundle.message("move.title"));
                    return null;
                }

                String question = String.format(
                        "File '%s' already exists. Do you want to move selected declarations to this file?",
                        filesExistingInTargetDir.get(0).getVirtualFile().getPath());
                int ret = Messages.showYesNoDialog(myProject, question, RefactoringBundle.message("move.title"),
                        Messages.getQuestionIcon());
                if (ret != Messages.YES)
                    return null;
            }

            // All source files must be in the same directory
            return new KotlinMoveTargetForDeferredFile(new FqName(getTargetPackage()),
                    moveDestination.getTargetIfExists(sourceFiles.get(0)), new Function1<KtFile, KtFile>() {
                        @Override
                        public KtFile invoke(@NotNull KtFile originalFile) {
                            return JetRefactoringUtilKt.getOrCreateKotlinFile(
                                    targetFileName != null ? targetFileName : originalFile.getName(),
                                    moveDestination.getTargetDirectory(originalFile));
                        }
                    });
        }

        final File targetFile = new File(getTargetFilePath());
        if (!checkTargetFileName(targetFile.getName()))
            return null;
        KtFile jetFile = (KtFile) JetRefactoringUtilKt.toPsiFile(targetFile, myProject);
        if (jetFile != null) {
            if (sourceFiles.size() == 1 && sourceFiles.contains(jetFile)) {
                setErrorText("Can't move to the original file");
                return null;
            }

            return new KotlinMoveTargetForExistingElement(jetFile);
        }

        File targetDir = targetFile.getParentFile();
        final PsiDirectory psiDirectory = targetDir != null
                ? JetRefactoringUtilKt.toPsiDirectory(targetDir, myProject)
                : null;
        if (psiDirectory == null) {
            setErrorText("No directory found for file: " + targetFile.getPath());
            return null;
        }

        Set<FqName> sourcePackageFqNames = CollectionsKt.mapTo(sourceFiles, new LinkedHashSet<FqName>(),
                new Function1<KtFile, FqName>() {
                    @Override
                    public FqName invoke(KtFile file) {
                        return file.getPackageFqName();
                    }
                });
        FqName targetPackageFqName = CollectionsKt.singleOrNull(sourcePackageFqNames);
        if (targetPackageFqName == null) {
            PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage(psiDirectory);
            if (psiPackage == null) {
                setErrorText("Could not find package corresponding to " + targetDir.getPath());
                return null;
            }
            targetPackageFqName = new FqName(psiPackage.getQualifiedName());
        }

        final String finalTargetPackageFqName = targetPackageFqName.asString();
        return new KotlinMoveTargetForDeferredFile(targetPackageFqName, psiDirectory,
                new Function1<KtFile, KtFile>() {
                    @Override
                    public KtFile invoke(@NotNull KtFile originalFile) {
                        return JetRefactoringUtilKt.getOrCreateKotlinFile(targetFile.getName(), psiDirectory,
                                finalTargetPackageFqName);
                    }
                });
    }

    @Nullable
    private String verifyBeforeRun() {
        if (memberTable.getSelectedMemberInfos().isEmpty())
            return "At least one member must be selected";

        if (isMoveToPackage()) {
            String name = getTargetPackage();
            if (name.length() != 0 && !PsiNameHelper.getInstance(myProject).isQualifiedName(name)) {
                return "\'" + name + "\' is invalid destination package name";
            }
        } else {
            PsiFile targetFile = JetRefactoringUtilKt.toPsiFile(new File(getTargetFilePath()), myProject);
            if (!(targetFile == null || targetFile instanceof KtFile)) {
                return KotlinRefactoringBundle.message("refactoring.move.non.kotlin.file");
            }
        }

        if (getSourceFiles(getSelectedElementsToMove()).size() == 1 && tfFileNameInPackage.getText().isEmpty()) {
            return "File name may not be empty";
        }

        return null;
    }

    private List<KtNamedDeclaration> getSelectedElementsToMove() {
        return CollectionsKt.map(memberTable.getSelectedMemberInfos(),
                new Function1<KotlinMemberInfo, KtNamedDeclaration>() {
                    @Override
                    public KtNamedDeclaration invoke(KotlinMemberInfo info) {
                        return info.getMember();
                    }
                });
    }

    @Override
    protected JComponent createCenterPanel() {
        return mainPanel;
    }

    @Override
    protected String getDimensionServiceKey() {
        return "#" + getClass().getName();
    }

    protected final String getTargetPackage() {
        return classPackageChooser.getText().trim();
    }

    protected final String getTargetFilePath() {
        return fileChooser.getText();
    }

    @Override
    protected void canRun() throws ConfigurationException {
        String message = verifyBeforeRun();
        if (message != null) {
            throw new ConfigurationException(message);
        }
    }

    @Override
    protected void doAction() {
        KotlinMoveTarget target = selectMoveTarget();
        if (target == null)
            return;

        saveRefactoringSettings();

        List<KtNamedDeclaration> elementsToMove = getSelectedElementsToMove();
        final List<KtFile> sourceFiles = getSourceFiles(elementsToMove);
        final PsiDirectory sourceDirectory = getSourceDirectory(sourceFiles);

        for (PsiElement element : elementsToMove) {
            String message = target.verify(element.getContainingFile());
            if (message != null) {
                CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), message, null,
                        myProject);
                return;
            }
        }

        try {
            boolean deleteSourceFile = false;

            if (isFullFileMove()) {
                if (isMoveToPackage()) {
                    final MoveDestination moveDestination = selectPackageBasedMoveDestination(false);
                    //noinspection ConstantConditions
                    PsiDirectory targetDir = moveDestination.getTargetIfExists(sourceDirectory);
                    final String targetFileName = sourceFiles.size() > 1 ? null : tfFileNameInPackage.getText();
                    List<PsiFile> filesExistingInTargetDir = getFilesExistingInTargetDir(sourceFiles,
                            targetFileName, targetDir);
                    if (filesExistingInTargetDir.isEmpty()) {
                        PsiDirectory targetDirectory = ApplicationUtilsKt
                                .runWriteAction(new Function0<PsiDirectory>() {
                                    @Override
                                    public PsiDirectory invoke() {
                                        return moveDestination.getTargetDirectory(sourceDirectory);
                                    }
                                });

                        for (KtFile sourceFile : sourceFiles) {
                            MoveUtilsKt.setUpdatePackageDirective(sourceFile,
                                    cbUpdatePackageDirective.isSelected());
                        }

                        invokeRefactoring(new MoveFilesOrDirectoriesProcessor(myProject,
                                sourceFiles.toArray(new PsiElement[sourceFiles.size()]), targetDirectory, true,
                                isSearchInComments(), isSearchInNonJavaFiles(), new MoveCallback() {
                                    @Override
                                    public void refactoringCompleted() {
                                        try {
                                            if (targetFileName != null) {
                                                CollectionsKt.single(sourceFiles).setName(targetFileName);
                                            }
                                        } finally {
                                            if (moveCallback != null) {
                                                moveCallback.refactoringCompleted();
                                            }
                                        }
                                    }
                                }, EmptyRunnable.INSTANCE) {
                            @Override
                            protected String getCommandName() {
                                return targetFileName != null
                                        ? "Move " + CollectionsKt.single(sourceFiles).getName()
                                        : "Move";
                            }

                            @Override
                            protected void performRefactoring(@NotNull UsageInfo[] usages) {
                                if (targetFileName != null) {
                                    KtFile sourceFile = CollectionsKt.single(sourceFiles);
                                    //noinspection ConstantConditions
                                    String temporaryName = UniqueNameGenerator.generateUniqueName("temp", "", ".kt",
                                            ArraysKt.map(sourceFile.getContainingDirectory().getFiles(),
                                                    new Function1<PsiFile, String>() {
                                                        @Override
                                                        public String invoke(PsiFile file) {
                                                            return file.getName();
                                                        }
                                                    }));
                                    sourceFile.setName(temporaryName);
                                }

                                super.performRefactoring(usages);
                            }
                        });

                        return;
                    }
                }

                int ret = Messages.showYesNoCancelDialog(myProject,
                        "You are about to move all declarations out of the source file(s). Do you want to delete empty files?",
                        RefactoringBundle.message("move.title"), Messages.getQuestionIcon());
                if (ret == Messages.CANCEL)
                    return;
                deleteSourceFile = ret == Messages.YES;
            }

            MoveDeclarationsDescriptor options = new MoveDeclarationsDescriptor(elementsToMove, target,
                    MoveDeclarationsDelegate.TopLevel.INSTANCE, isSearchInComments(), isSearchInNonJavaFiles(),
                    true, deleteSourceFile, moveCallback, false);
            invokeRefactoring(new MoveKotlinDeclarationsProcessor(myProject, options, Mover.Default.INSTANCE));
        } catch (IncorrectOperationException e) {
            CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.getMessage(), null,
                    myProject);
        }
    }

    private boolean isSearchInNonJavaFiles() {
        return cbSearchTextOccurrences.isSelected();
    }

    private boolean isSearchInComments() {
        return cbSearchInComments.isSelected();
    }

    private boolean isMoveToPackage() {
        return rbMoveToPackage.isSelected();
    }

    @Override
    public JComponent getPreferredFocusedComponent() {
        return classPackageChooser.getChildComponent();
    }
}