/*
* 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.ide.util.DirectoryChooser;
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.Pair;
import com.intellij.openapi.util.Pass;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.refactoring.*;
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.AutocreatingSingleSourceRootMoveDestination;
import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox;
import com.intellij.refactoring.move.moveClassesOrPackages.MultipleRootsMoveDestination;
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.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ui.UIUtil;
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.KotlinRefactoringBundle;
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtilKt;
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 final MoveCallback moveCallback;
private final PsiDirectory initialTargetDirectory;
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;
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;
this.initialTargetDirectory = targetDirectory;
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;
}
@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);
}
}
)
);
}
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 = KotlinRefactoringUtilKt.toPsiFile(targetFile, myProject);
if (targetPsiFile instanceof KtFile) {
dialog.select((KtFile) targetPsiFile);
}
else {
PsiDirectory targetDir = KotlinRefactoringUtilKt.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 Pair<VirtualFile, ? extends MoveDestination> selectPackageBasedTargetDirAndDestination(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;
}
DirectoryChooser.ItemWrapper selectedItem = (DirectoryChooser.ItemWrapper)destinationFolderCB.getComboBox().getSelectedItem();
PsiDirectory selectedPsiDirectory = selectedItem != null ? selectedItem.getDirectory() : null;
if (selectedPsiDirectory == null) {
if (initialTargetDirectory != null) {
selectedPsiDirectory = initialTargetDirectory;
}
else {
return Pair.create(null, new MultipleRootsMoveDestination(targetPackage));
}
}
VirtualFile targetDirectory = selectedPsiDirectory.getVirtualFile();
return Pair.create(targetDirectory, new AutocreatingSingleSourceRootMoveDestination(targetPackage, targetDirectory));
}
private boolean checkTargetFileName(String fileName) {
if (FileTypeManager.getInstance().getFileTypeByFileName(fileName) == KotlinFileType.INSTANCE) return true;
setErrorText("Can't move to non-Kotlin file");
return false;
}
@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()) {
Pair<VirtualFile, ? extends MoveDestination> targetDirWithMoveDestination = selectPackageBasedTargetDirAndDestination(true);
if (targetDirWithMoveDestination == null) return null;
VirtualFile targetDir = targetDirWithMoveDestination.getFirst();
final MoveDestination moveDestination = targetDirWithMoveDestination.getSecond();
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 (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;
}
PsiFile targetFile = filesExistingInTargetDir.get(0);
if (!sourceFiles.contains(targetFile)) {
String question = String.format(
"File '%s' already exists. Do you want to move selected declarations to this file?",
targetFile.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)),
targetDir,
new Function1<KtFile, KtFile>() {
@Override
public KtFile invoke(@NotNull KtFile originalFile) {
return KotlinRefactoringUtilKt.getOrCreateKotlinFile(
targetFileName != null ? targetFileName : originalFile.getName(),
moveDestination.getTargetDirectory(originalFile)
);
}
}
);
}
final File targetFile = new File(getTargetFilePath());
if (!checkTargetFileName(targetFile.getName())) return null;
KtFile jetFile = (KtFile) KotlinRefactoringUtilKt.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 ? KotlinRefactoringUtilKt.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,
null,
new Function1<KtFile, KtFile>() {
@Override
public KtFile invoke(@NotNull KtFile originalFile) {
return KotlinRefactoringUtilKt.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 = KotlinRefactoringUtilKt.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();
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()) {
Pair<VirtualFile, ? extends MoveDestination> sourceRootWithMoveDestination = selectPackageBasedTargetDirAndDestination(false);
//noinspection ConstantConditions
final MoveDestination moveDestination = sourceRootWithMoveDestination.getSecond();
PsiDirectory targetDir = moveDestination.getTargetIfExists(sourceDirectory);
String targetFileName = sourceFiles.size() > 1 ? null : tfFileNameInPackage.getText();
List<PsiFile> filesExistingInTargetDir = getFilesExistingInTargetDir(sourceFiles, targetFileName, targetDir);
if (filesExistingInTargetDir.isEmpty()
|| (filesExistingInTargetDir.size() == 1 && sourceFiles.contains(filesExistingInTargetDir.get(0)))) {
PsiDirectory targetDirectory = ApplicationUtilsKt.runWriteAction(
new Function0<PsiDirectory>() {
@Override
public PsiDirectory invoke() {
return moveDestination.getTargetDirectory(sourceDirectory);
}
}
);
for (KtFile sourceFile : sourceFiles) {
MoveUtilsKt.setUpdatePackageDirective(sourceFile, cbUpdatePackageDirective.isSelected());
}
BaseRefactoringProcessor processor;
processor = sourceFiles.size() == 1 && targetFileName != null
? new MoveToKotlinFileProcessor(myProject,
CollectionsKt.single(sourceFiles),
targetDirectory,
targetFileName,
isSearchInComments(),
isSearchInNonJavaFiles(),
moveCallback)
: new KotlinAwareMoveFilesOrDirectoriesProcessor(myProject,
sourceFiles,
targetDirectory,
isSearchInComments(),
isSearchInNonJavaFiles(),
moveCallback);
invokeRefactoring(processor);
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(
myProject,
elementsToMove,
target,
MoveDeclarationsDelegate.TopLevel.INSTANCE,
isSearchInComments(),
isSearchInNonJavaFiles(),
false,
deleteSourceFile,
moveCallback,
false
);
invokeRefactoring(new MoveKotlinDeclarationsProcessor(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();
}
private static class MemberInfoModelImpl extends AbstractMemberInfoModel<KtNamedDeclaration, KotlinMemberInfo> {
}
}