blob: 844eb2dc0611502d3137320e604c416731d5b1fd [file] [log] [blame]
/*
* Copyright 2000-2014 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.
*/
/**
* created at Nov 27, 2001
* @author Jeka
*/
package com.intellij.refactoring.move.moveClassesOrPackages;
import com.intellij.history.LocalHistory;
import com.intellij.history.LocalHistoryAction;
import com.intellij.ide.util.DirectoryChooser;
import com.intellij.ide.util.PlatformPackageUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.JavaProjectRootsUtil;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.refactoring.*;
import com.intellij.refactoring.move.MoveCallback;
import com.intellij.refactoring.rename.DirectoryAsPackageRenameHandlerBase;
import com.intellij.refactoring.rename.RenameUtil;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.refactoring.util.*;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MoveClassesOrPackagesImpl {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesImpl");
public static void doMove(final Project project,
PsiElement[] adjustedElements,
PsiElement initialTargetElement,
final MoveCallback moveCallback) {
if (!CommonRefactoringUtil.checkReadOnlyStatusRecursively(project, Arrays.asList(adjustedElements), true)) {
return;
}
final String initialTargetPackageName = getInitialTargetPackageName(initialTargetElement, adjustedElements);
final PsiDirectory initialTargetDirectory = getInitialTargetDirectory(initialTargetElement, adjustedElements);
final boolean isTargetDirectoryFixed = initialTargetDirectory == null;
boolean searchTextOccurences = false;
for (int i = 0; i < adjustedElements.length && !searchTextOccurences; i++) {
PsiElement psiElement = adjustedElements[i];
searchTextOccurences = TextOccurrencesUtil.isSearchTextOccurencesEnabled(psiElement);
}
final MoveClassesOrPackagesDialog moveDialog =
new MoveClassesOrPackagesDialog(project, searchTextOccurences, adjustedElements, initialTargetElement, moveCallback);
boolean searchInComments = JavaRefactoringSettings.getInstance().MOVE_SEARCH_IN_COMMENTS;
boolean searchForTextOccurences = JavaRefactoringSettings.getInstance().MOVE_SEARCH_FOR_TEXT;
moveDialog.setData(adjustedElements, initialTargetPackageName, initialTargetDirectory, isTargetDirectoryFixed, initialTargetElement == null, searchInComments,
searchForTextOccurences, HelpID.getMoveHelpID(adjustedElements[0]));
moveDialog.show();
}
@Nullable
public static PsiElement[] adjustForMove(final Project project, final PsiElement[] elements, final PsiElement targetElement) {
final PsiElement[] psiElements = new PsiElement[elements.length];
List<String> names = new ArrayList<String>();
for (int idx = 0; idx < elements.length; idx++) {
PsiElement element = elements[idx];
if (element instanceof PsiDirectory) {
PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)element);
LOG.assertTrue(aPackage != null);
if (aPackage.getQualifiedName().isEmpty()) { //is default package
String message = RefactoringBundle.message("move.package.refactoring.cannot.be.applied.to.default.package");
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("move.title"), message, HelpID.getMoveHelpID(element), project);
return null;
}
if (!checkNesting(project, aPackage, targetElement, true)) return null;
if (!isAlreadyChecked(psiElements, idx, aPackage) && !checkMovePackage(project, aPackage)) return null;
element = aPackage;
}
else if (element instanceof PsiPackage) {
final PsiPackage psiPackage = (PsiPackage)element;
if (!checkNesting(project, psiPackage, targetElement, true)) return null;
if (!checkMovePackage(project, psiPackage)) return null;
}
else if (element instanceof PsiClass) {
PsiClass aClass = (PsiClass)element;
if (aClass instanceof PsiAnonymousClass) {
String message = RefactoringBundle.message("move.class.refactoring.cannot.be.applied.to.anonymous.classes");
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("move.title"), message, HelpID.getMoveHelpID(element), project);
return null;
}
if (isClassInnerOrLocal(aClass)) {
String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("moving.local.classes.is.not.supported"));
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("move.title"), message, HelpID.getMoveHelpID(element), project);
return null;
}
String name = null;
for (MoveClassHandler nameProvider : MoveClassHandler.EP_NAME.getExtensions()) {
name = nameProvider.getName(aClass);
if (name != null) break;
}
if (name == null) name = aClass.getContainingFile().getName();
if (names.contains(name)) {
String message = RefactoringBundle
.getCannotRefactorMessage(RefactoringBundle.message("there.are.going.to.be.multiple.destination.files.with.the.same.name"));
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("move.title"), message, HelpID.getMoveHelpID(element), project);
return null;
}
names.add(name);
}
psiElements[idx] = element;
}
return psiElements;
}
static boolean isClassInnerOrLocal(PsiClass aClass) {
return aClass.getContainingClass() != null || aClass.getQualifiedName() == null;
}
private static boolean isAlreadyChecked(PsiElement[] psiElements, int idx, PsiPackage aPackage) {
for (int i = 0; i < idx; i++) {
if (Comparing.equal(psiElements[i], aPackage)) {
return true;
}
}
return false;
}
private static boolean checkMovePackage(Project project, PsiPackage aPackage) {
final PsiDirectory[] directories = aPackage.getDirectories();
final VirtualFile[] virtualFiles = aPackage.occursInPackagePrefixes();
if (directories.length > 1 || virtualFiles.length > 0) {
final StringBuffer message = new StringBuffer();
RenameUtil.buildPackagePrefixChangedMessage(virtualFiles, message, aPackage.getQualifiedName());
if (directories.length > 1) {
DirectoryAsPackageRenameHandlerBase.buildMultipleDirectoriesInPackageMessage(message, aPackage.getQualifiedName(), directories);
message.append("\n\n");
String report = RefactoringBundle
.message("all.these.directories.will.be.moved.and.all.references.to.0.will.be.changed", aPackage.getQualifiedName());
message.append(report);
}
message.append("\n");
message.append(RefactoringBundle.message("do.you.wish.to.continue"));
int ret =
Messages.showYesNoDialog(project, message.toString(), RefactoringBundle.message("warning.title"), Messages.getWarningIcon());
if (ret != Messages.YES) {
return false;
}
}
return true;
}
static boolean checkNesting(final Project project, final PsiPackage srcPackage, final PsiElement targetElement, boolean showError) {
final PsiPackage targetPackage = targetElement instanceof PsiPackage
? (PsiPackage)targetElement
: targetElement instanceof PsiDirectory ? JavaDirectoryService.getInstance()
.getPackage((PsiDirectory)targetElement) : null;
for (PsiPackage curPackage = targetPackage; curPackage != null; curPackage = curPackage.getParentPackage()) {
if (curPackage.equals(srcPackage)) {
if (showError) {
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("move.title"),
RefactoringBundle.message("cannot.move.package.into.itself"),
HelpID.getMoveHelpID(srcPackage), project);
}
return false;
}
}
return true;
}
public static String getInitialTargetPackageName(PsiElement initialTargetElement, final PsiElement[] movedElements) {
String name = getContainerPackageName(initialTargetElement);
if (name == null) {
if (movedElements != null) {
name = getTargetPackageNameForMovedElement(movedElements[0]);
}
if (name == null) {
final PsiDirectory commonDirectory = getCommonDirectory(movedElements);
if (commonDirectory != null && JavaDirectoryService.getInstance().getPackage(commonDirectory) != null) {
name = JavaDirectoryService.getInstance().getPackage(commonDirectory).getQualifiedName();
}
}
}
if (name == null) {
name = "";
}
return name;
}
@Nullable
private static PsiDirectory getCommonDirectory(PsiElement[] movedElements) {
PsiDirectory commonDirectory = null;
for (PsiElement movedElement : movedElements) {
final PsiFile containingFile = movedElement.getContainingFile();
if (containingFile != null) {
final PsiDirectory containingDirectory = containingFile.getContainingDirectory();
if (containingDirectory != null) {
if (commonDirectory == null) {
commonDirectory = containingDirectory;
}
else {
if (commonDirectory != containingDirectory) {
return null;
}
}
}
}
}
if (commonDirectory != null) {
return commonDirectory;
}
else {
return null;
}
}
private static String getContainerPackageName(final PsiElement psiElement) {
if (psiElement instanceof PsiPackage) {
return ((PsiPackage)psiElement).getQualifiedName();
}
else if (psiElement instanceof PsiDirectory) {
PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)psiElement);
return aPackage != null ? aPackage.getQualifiedName() : "";
}
else if (psiElement != null) {
PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(psiElement.getContainingFile().getContainingDirectory());
return aPackage != null ? aPackage.getQualifiedName() : "";
}
else {
return null;
}
}
private static String getTargetPackageNameForMovedElement(final PsiElement psiElement) {
if (psiElement instanceof PsiPackage) {
final PsiPackage psiPackage = (PsiPackage)psiElement;
final PsiPackage parentPackage = psiPackage.getParentPackage();
return parentPackage != null ? parentPackage.getQualifiedName() : "";
}
else if (psiElement instanceof PsiDirectory) {
PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)psiElement);
return aPackage != null ? getTargetPackageNameForMovedElement(aPackage) : "";
}
else if (psiElement != null) {
PsiDirectory directory = PlatformPackageUtil.getDirectory(psiElement);
PsiPackage aPackage = directory == null ? null : JavaDirectoryService.getInstance().getPackage(directory);
return aPackage != null ? aPackage.getQualifiedName() : "";
}
else {
return null;
}
}
public static PsiDirectory getInitialTargetDirectory(PsiElement initialTargetElement, final PsiElement[] movedElements) {
PsiDirectory initialTargetDirectory = getContainerDirectory(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
public static PsiDirectory getContainerDirectory(final PsiElement psiElement) {
if (psiElement instanceof PsiPackage) {
final PsiDirectory[] directories = ((PsiPackage)psiElement).getDirectories();
return directories.length == 1 ? directories[0] : null; //??
}
if (psiElement instanceof PsiDirectory) {
return (PsiDirectory)psiElement;
}
if (psiElement != null) {
return psiElement.getContainingFile().getContainingDirectory();
}
return null;
}
public static void doRearrangePackage(final Project project, final PsiDirectory[] directories) {
if (!CommonRefactoringUtil.checkReadOnlyStatusRecursively(project, Arrays.asList(directories), true)) {
return;
}
List<PsiDirectory> sourceRootDirectories = buildRearrangeTargetsList(project, directories);
DirectoryChooser chooser = new DirectoryChooser(project);
chooser.setTitle(RefactoringBundle.message("select.source.root.chooser.title"));
chooser.fillList(sourceRootDirectories.toArray(new PsiDirectory[sourceRootDirectories.size()]), null, project, "");
chooser.show();
if (!chooser.isOK()) return;
final PsiDirectory selectedTarget = chooser.getSelectedDirectory();
if (selectedTarget == null) return;
final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
final Runnable analyzeConflicts = new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
RefactoringConflictsUtil
.analyzeModuleConflicts(project, Arrays.asList(directories), UsageInfo.EMPTY_ARRAY, selectedTarget, conflicts);
}
});
}
};
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(analyzeConflicts, "Analyze Module Conflicts...", true, project)) {
return;
}
if (!conflicts.isEmpty()) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
throw new BaseRefactoringProcessor.ConflictsInTestsException(conflicts.values());
}
else {
final ConflictsDialog conflictsDialog = new ConflictsDialog(project, conflicts);
conflictsDialog.show();
if (!conflictsDialog.isOK()) {
return;
}
}
}
final Ref<IncorrectOperationException> ex = Ref.create(null);
final String commandDescription = RefactoringBundle.message("moving.directories.command");
Runnable runnable = new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
LocalHistoryAction a = LocalHistory.getInstance().startAction(commandDescription);
try {
rearrangeDirectoriesToTarget(directories, selectedTarget);
}
catch (IncorrectOperationException e) {
ex.set(e);
}
finally {
a.finish();
}
}
});
}
};
CommandProcessor.getInstance().executeCommand(project, runnable, commandDescription, null);
if (ex.get() != null) {
RefactoringUIUtil.processIncorrectOperation(project, ex.get());
}
}
private static List<PsiDirectory> buildRearrangeTargetsList(final Project project, final PsiDirectory[] directories) {
final List<VirtualFile> sourceRoots = JavaProjectRootsUtil.getSuitableDestinationSourceRoots(project);
List<PsiDirectory> sourceRootDirectories = new ArrayList<PsiDirectory>();
sourceRoots:
for (final VirtualFile sourceRoot : sourceRoots) {
PsiDirectory sourceRootDirectory = PsiManager.getInstance(project).findDirectory(sourceRoot);
if (sourceRootDirectory == null) continue;
final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(sourceRootDirectory);
if (aPackage == null) continue;
final String packagePrefix = aPackage.getQualifiedName();
for (final PsiDirectory directory : directories) {
String qualifiedName = JavaDirectoryService.getInstance().getPackage(directory).getQualifiedName();
if (!qualifiedName.startsWith(packagePrefix)) {
continue sourceRoots;
}
}
sourceRootDirectories.add(sourceRootDirectory);
}
return sourceRootDirectories;
}
private static void rearrangeDirectoriesToTarget(PsiDirectory[] directories, PsiDirectory selectedTarget)
throws IncorrectOperationException {
final VirtualFile sourceRoot = selectedTarget.getVirtualFile();
for (PsiDirectory directory : directories) {
final PsiPackage parentPackage = JavaDirectoryService.getInstance().getPackage(directory).getParentPackage();
final PackageWrapper wrapper = new PackageWrapper(parentPackage);
final PsiDirectory moveTarget = RefactoringUtil.createPackageDirectoryInSourceRoot(wrapper, sourceRoot);
MoveClassesOrPackagesUtil.moveDirectoryRecursively(directory, moveTarget);
}
}
}