From a2d58c893c0d9f55c69623701fbc8c24dbee74ef Mon Sep 17 00:00:00 2001 From: anna Date: Thu, 7 Jan 2010 18:19:33 +0300 Subject: [PATCH] rename/move package as everything inside directory --- ...ClassesOrPackagesToNewDirectoryDialog.java | 11 + .../MoveDirectoryWithClassesProcessor.java | 277 ++++++++++++++++++ .../MovePackagesHandler.java | 81 +++-- .../DirectoryAsPackageRenameHandler.java | 104 +++++-- .../after/src1/pack2/UsagesFromBoth.java | 9 + .../movePackage/after/src1/target/empty.txt | 0 .../after/src1/target/pack1/S1.java | 3 + .../movePackage/after/src2/pack1/S2.java | 3 + .../after/src2/pack2/UsagesFromBoth2.java | 9 + .../movePackage/before/src1/pack1/S1.java | 3 + .../before/src1/pack2/UsagesFromBoth.java | 8 + .../movePackage/before/src1/target/empty.txt | 0 .../movePackage/before/src2/pack1/S2.java | 3 + .../before/src2/pack2/UsagesFromBoth2.java | 8 + .../after/src1/pack2/UsagesFromBoth.java | 11 + .../after/src1/target/empty.txt | 0 .../after/src1/target/pack1/S1.java | 3 + .../after/src1/target/pack1/subpack/SS.java | 3 + .../after/src2/pack1/S2.java | 3 + .../after/src2/pack2/UsagesFromBoth2.java | 9 + .../before/src1/pack1/S1.java | 3 + .../before/src1/pack1/subpack/SS.java | 3 + .../before/src1/pack2/UsagesFromBoth.java | 10 + .../before/src1/target/empty.txt | 0 .../before/src2/pack1/S2.java | 3 + .../before/src2/pack2/UsagesFromBoth2.java | 8 + .../MovePackageAsDirectoryTest.java | 82 ++++++ .../src/messages/RefactoringBundle.properties | 2 + 28 files changed, 598 insertions(+), 61 deletions(-) create mode 100644 java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveDirectoryWithClassesProcessor.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src1/pack2/UsagesFromBoth.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src1/target/empty.txt create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src1/target/pack1/S1.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src2/pack1/S2.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src2/pack2/UsagesFromBoth2.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src1/pack1/S1.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src1/pack2/UsagesFromBoth.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src1/target/empty.txt create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src2/pack1/S2.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src2/pack2/UsagesFromBoth2.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/pack2/UsagesFromBoth.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/target/empty.txt create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/target/pack1/S1.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/target/pack1/subpack/SS.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src2/pack1/S2.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src2/pack2/UsagesFromBoth2.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/pack1/S1.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/pack1/subpack/SS.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/pack2/UsagesFromBoth.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/target/empty.txt create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src2/pack1/S2.java create mode 100644 java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src2/pack2/UsagesFromBoth2.java create mode 100644 java/java-tests/testSrc/com/intellij/refactoring/MovePackageAsDirectoryTest.java diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesToNewDirectoryDialog.java b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesToNewDirectoryDialog.java index 32c7f0a48a20..a9e8bab3bfc1 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesToNewDirectoryDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesToNewDirectoryDialog.java @@ -150,6 +150,17 @@ public class MoveClassesOrPackagesToNewDirectoryDialog extends DialogWrapper { refactoringSettings.MOVE_SEARCH_IN_COMMENTS = searchInComments; refactoringSettings.MOVE_SEARCH_FOR_TEXT = searchForTextOccurences; + performRefactoring(project, directory, aPackage, searchInComments, searchForTextOccurences); + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myDestDirectoryField.getTextField(); + } + + protected void performRefactoring(Project project, PsiDirectory directory, PsiPackage aPackage, + boolean searchInComments, + boolean searchForTextOccurences) { final VirtualFile sourceRoot = ProjectRootManager.getInstance(project).getFileIndex().getSourceRootForFile(directory.getVirtualFile()); if (sourceRoot == null) { Messages.showErrorDialog(project, RefactoringBundle.message("destination.directory.does.not.correspond.to.any.package"), diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveDirectoryWithClassesProcessor.java b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveDirectoryWithClassesProcessor.java new file mode 100644 index 000000000000..b2cf0d2b546d --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveDirectoryWithClassesProcessor.java @@ -0,0 +1,277 @@ +/* + * 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. + */ + +/* + * User: anna + * Date: 28-Dec-2009 + */ +package com.intellij.refactoring.move.moveClassesOrPackages; + +import com.intellij.codeInsight.ChangeContextUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.psi.*; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.BaseRefactoringProcessor; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.listeners.RefactoringElementListener; +import com.intellij.refactoring.move.MoveCallback; +import com.intellij.refactoring.rename.RenameUtil; +import com.intellij.refactoring.util.NonCodeUsageInfo; +import com.intellij.refactoring.util.RefactoringUIUtil; +import com.intellij.usageView.UsageInfo; +import com.intellij.usageView.UsageViewDescriptor; +import com.intellij.usageView.UsageViewUtil; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class MoveDirectoryWithClassesProcessor extends BaseRefactoringProcessor { + private final PsiDirectory[] myDirectories; + private PsiDirectory myTargetDirectory; + private boolean mySearchInComments; + private boolean mySearchInNonJavaFiles; + private Map myClassesToMove; + private NonCodeUsageInfo[] myNonCodeUsages; + private MoveCallback myMoveCallback; + + public MoveDirectoryWithClassesProcessor(Project project, + PsiDirectory[] directories, + PsiDirectory targetDirectory, + boolean searchInComments, + boolean searchInNonJavaFiles, + boolean includeSelf, + MoveCallback moveCallback) { + super(project); + myDirectories = directories; + myTargetDirectory = targetDirectory; + mySearchInComments = searchInComments; + mySearchInNonJavaFiles = searchInNonJavaFiles; + myMoveCallback = moveCallback; + myClassesToMove = new HashMap(); + for (PsiDirectory dir : directories) { + collectClasses(myClassesToMove, dir, includeSelf ? dir.getParentDirectory() : dir, getTargetDirectory(dir)); + } + } + + @Override + protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) { + PsiElement[] elements = new PsiElement[myClassesToMove.size()]; + final PsiClass[] classes = myClassesToMove.keySet().toArray(new PsiClass[myClassesToMove.keySet().size()]); + System.arraycopy(classes, 0, elements, 0, classes.length); + return new MoveClassesOrPackagesViewDescriptor(elements, false, false, getTargetName()); + } + + protected String getTargetName() { + return RefactoringUIUtil.getDescription(getTargetDirectory(null).getTargetDirectory(), false); + } + + @NotNull + @Override + public UsageInfo[] findUsages() { + final List usages = new ArrayList(); + final Set packageNames = new HashSet(); + for (PsiClass psiClass : myClassesToMove.keySet()) { + Collections.addAll(usages, MoveClassesOrPackagesUtil.findUsages(psiClass, mySearchInComments, mySearchInNonJavaFiles, psiClass.getName())); + final String fqName = psiClass.getQualifiedName(); + assert fqName != null; + packageNames.add(StringUtil.getPackageName(fqName)); + } + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(myProject); + for (String packageName : packageNames) { + final PsiPackage aPackage = psiFacade.findPackage(packageName); + if (aPackage != null) { + boolean remainsNothing = true; + for (PsiDirectory packageDirectory : aPackage.getDirectories()) { + if (!isUnderRefactoring(packageDirectory)) { + remainsNothing = false; + break; + } + } + if (remainsNothing) { + for (PsiReference reference : ReferencesSearch.search(aPackage)) { + final PsiElement element = reference.getElement(); + final PsiImportStatementBase statementBase = PsiTreeUtil.getParentOfType(element, PsiImportStatementBase.class); + if (statementBase != null && statementBase.isOnDemand()) { + usages.add(new RemoveOnDemandImportStatementsUsageInfo(statementBase)); + } + } + } + } + } + return UsageViewUtil.removeDuplicatedUsages(usages.toArray(new UsageInfo[usages.size()])); + } + + private boolean isUnderRefactoring(PsiDirectory packageDirectory) { + for (PsiDirectory directory : myDirectories) { + if (PsiTreeUtil.isAncestor(directory, packageDirectory, true)) { + return true; + } + } + return false; + } + + + @Override + protected boolean preprocessUsages(Ref refUsages) { + final MultiMap conflicts = new MultiMap(); + for (PsiClass psiClass : myClassesToMove.keySet()) { + try { + myClassesToMove.get(psiClass).checkMove(psiClass); + } + catch (IncorrectOperationException e) { + conflicts.putValue(psiClass, e.getMessage()); + } + } + return showConflicts(conflicts); + } + + @Override + protected void refreshElements(PsiElement[] elements) {} + + @Override + public void performRefactoring(UsageInfo[] usages) { + final Map oldToNewElementsMapping = new HashMap(); + for (PsiClass psiClass : myClassesToMove.keySet()) { + ChangeContextUtil.encodeContextInfo(psiClass, true); + final RefactoringElementListener listener = getTransaction().getElementListener(psiClass); + final PsiClass newClass = MoveClassesOrPackagesUtil.doMoveClass(psiClass, myClassesToMove.get(psiClass).findOrCreateTargetDirectory()); + oldToNewElementsMapping.put(psiClass, newClass); + listener.elementMoved(newClass); + } + for (PsiClass psiClass : myClassesToMove.keySet()) { + ChangeContextUtil.decodeContextInfo(psiClass, null, null); + } + myNonCodeUsages = MoveClassesOrPackagesProcessor.retargetUsages(usages, oldToNewElementsMapping); + for (UsageInfo usage : usages) { + if (usage instanceof RemoveOnDemandImportStatementsUsageInfo) { + final PsiElement element = usage.getElement(); + if (element != null) { + element.delete(); + } + } + } + for (PsiDirectory directory : myDirectories) { + directory.delete(); + } + } + + @Override + protected void performPsiSpoilingRefactoring() { + RenameUtil.renameNonCodeUsages(myProject, myNonCodeUsages); + if (myMoveCallback != null) { + myMoveCallback.refactoringCompleted(); + } + } + + private static void collectClasses(Map classesToMove, + PsiDirectory directory, + PsiDirectory rootDirectory, + @NotNull TargetDirectoryWrapper targetDirectory) { + final PsiElement[] children = directory.getChildren(); + final String relativePath = VfsUtil.getRelativePath(directory.getVirtualFile(), rootDirectory.getVirtualFile(), '/'); + + final TargetDirectoryWrapper newTargetDirectory = relativePath.length() == 0 + ? targetDirectory + : targetDirectory.findOrCreateChild(relativePath); + for (PsiElement child : children) { + if (child instanceof PsiJavaFile) { + for (PsiClass aClass : ((PsiJavaFile)child).getClasses()) { + classesToMove.put(aClass, newTargetDirectory); + } + } + else if (child instanceof PsiDirectory){ + collectClasses(classesToMove, (PsiDirectory)child, directory, newTargetDirectory); + } + } + } + + @Override + protected String getCommandName() { + return RefactoringBundle.message("moving.directories.command"); + } + + public TargetDirectoryWrapper getTargetDirectory(PsiDirectory dir) { + return new TargetDirectoryWrapper(myTargetDirectory); + } + + private static class RemoveOnDemandImportStatementsUsageInfo extends UsageInfo { + public RemoveOnDemandImportStatementsUsageInfo(PsiImportStatementBase statementBase) { + super(statementBase); + } + } + + public static class TargetDirectoryWrapper { + private TargetDirectoryWrapper myParentDirectory; + private PsiDirectory myTargetDirectory; + private String myRelativePath; + + public TargetDirectoryWrapper(PsiDirectory targetDirectory) { + myTargetDirectory = targetDirectory; + } + + public TargetDirectoryWrapper(TargetDirectoryWrapper parentDirectory, String relativePath) { + myParentDirectory = parentDirectory; + myRelativePath = relativePath; + } + + public TargetDirectoryWrapper(PsiDirectory parentDirectory, String relativePath) { + myTargetDirectory = parentDirectory.findSubdirectory(relativePath); + //in case it was null + myParentDirectory = new TargetDirectoryWrapper(parentDirectory); + myRelativePath = relativePath; + } + + public PsiDirectory findOrCreateTargetDirectory() { + if (myTargetDirectory == null) { + final PsiDirectory root = myParentDirectory.findOrCreateTargetDirectory(); + + myTargetDirectory = root.findSubdirectory(myRelativePath); + if (myTargetDirectory == null) { + myTargetDirectory = root.createSubdirectory(myRelativePath); + } + } + return myTargetDirectory; + } + + @Nullable + public PsiDirectory getTargetDirectory() { + return myTargetDirectory; + } + + public TargetDirectoryWrapper findOrCreateChild(String relativePath) { + if (myTargetDirectory != null) { + final PsiDirectory psiDirectory = myTargetDirectory.findSubdirectory(relativePath); + if (psiDirectory != null) { + return new TargetDirectoryWrapper(psiDirectory); + } + } + return new TargetDirectoryWrapper(this, relativePath); + } + + public void checkMove(PsiClass psiClass) throws IncorrectOperationException { + if (myTargetDirectory != null) { + psiClass.getManager().checkMove(psiClass, myTargetDirectory); + } + } + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MovePackagesHandler.java b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MovePackagesHandler.java index c07c8ca6dfde..136d2f94b3f0 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MovePackagesHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MovePackagesHandler.java @@ -19,21 +19,20 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.*; -import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.JavaDirectoryService; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiPackage; +import com.intellij.refactoring.JavaRefactoringSettings; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.move.MoveCallback; -import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil; -import com.intellij.refactoring.util.MoveRenameUsageInfo; import com.intellij.refactoring.util.RadioUpDownListener; import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.containers.HashSet; import javax.swing.*; import java.awt.*; -import java.util.ArrayList; public class MovePackagesHandler extends MoveClassesOrPackagesHandlerBase { private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.move.moveClassesOrPackages.MovePackagesHandler"); @@ -50,10 +49,10 @@ public class MovePackagesHandler extends MoveClassesOrPackagesHandlerBase { } public void doMove(final Project project, final PsiElement[] elements, final PsiElement targetContainer, final MoveCallback callback) { - if (targetContainer == null && canMoveOrRearrangePackages(elements) ) { - PsiDirectory[] directories = new PsiDirectory[elements.length]; + if (canMoveOrRearrangePackages(elements) ) { + final PsiDirectory[] directories = new PsiDirectory[elements.length]; System.arraycopy(elements, 0, directories, 0, directories.length); - SelectMoveOrRearrangePackageDialog dialog = new SelectMoveOrRearrangePackageDialog(project, directories); + SelectMoveOrRearrangePackageDialog dialog = new SelectMoveOrRearrangePackageDialog(project, directories, targetContainer == null); dialog.show(); if (!dialog.isOK()) return; @@ -63,40 +62,29 @@ public class MovePackagesHandler extends MoveClassesOrPackagesHandlerBase { } if (dialog.isMoveDirectory()) { - final ArrayList usages = new ArrayList(); - for (PsiDirectory directory : directories) { - final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(directory); - if (aPackage != null) { - for (PsiReference reference : ReferencesSearch.search(aPackage, directory.getUseScope())) { - usages.add(new MoveRenameUsageInfo(reference, aPackage)); - } - } + if (targetContainer instanceof PsiDirectory) { + final JavaRefactoringSettings refactoringSettings = JavaRefactoringSettings.getInstance(); + final MoveDirectoryWithClassesProcessor processor = + new MoveDirectoryWithClassesProcessor(project, directories, (PsiDirectory)targetContainer, + refactoringSettings.RENAME_SEARCH_IN_COMMENTS_FOR_PACKAGE, + refactoringSettings.RENAME_SEARCH_IN_COMMENTS_FOR_PACKAGE, true, callback); + processor.run(); } - - final PsiElement[] targetDirectory = new PsiElement[]{targetContainer}; - - MoveFilesOrDirectoriesUtil.doMove(project, elements, targetDirectory, new MoveCallback() { - public void refactoringCompleted() { - if (callback != null) callback.refactoringCompleted(); - if (targetDirectory[0] instanceof PsiDirectory) { - final PsiPackage destPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)targetDirectory[0]); - if (destPackage != null) { - for (MoveRenameUsageInfo usage : usages) { - final PsiPackage aPackage = (PsiPackage)usage.getReferencedElement(); - if (aPackage != null) { - final PsiReference reference = usage.getReference(); - if (reference != null) { - final PsiPackage pack = JavaPsiFacade.getInstance(project).findPackage(StringUtil.getQualifiedName(destPackage.getQualifiedName(), aPackage.getName())); - if (pack != null) { - reference.bindToElement(pack); - } - } - } - } - } + else { + new MoveClassesOrPackagesToNewDirectoryDialog(directories[0], new PsiElement[2], callback) { + @Override + protected void performRefactoring(Project project, + final PsiDirectory targetDirectory, + PsiPackage aPackage, + boolean searchInComments, + boolean searchForTextOccurences) { + final MoveDirectoryWithClassesProcessor processor = + new MoveDirectoryWithClassesProcessor(project, directories, targetDirectory, searchInComments, searchForTextOccurences, + true, callback); + processor.run(); } - } - }); + }.show(); + } return; } } @@ -131,10 +119,16 @@ public class MovePackagesHandler extends MoveClassesOrPackagesHandlerBase { private JRadioButton myRbMoveDirectory; private final PsiDirectory[] myDirectories; + private final boolean myRearrangePackagesEnabled; public SelectMoveOrRearrangePackageDialog(Project project, PsiDirectory[] directories) { + this(project, directories, true); + } + + public SelectMoveOrRearrangePackageDialog(Project project, PsiDirectory[] directories, boolean rearrangePackagesEnabled) { super(project, true); myDirectories = directories; + myRearrangePackagesEnabled = rearrangePackagesEnabled; setTitle(RefactoringBundle.message("select.refactoring.title")); init(); } @@ -184,13 +178,14 @@ public class MovePackagesHandler extends MoveClassesOrPackagesHandlerBase { } myRbRearrangePackage = new JRadioButton(); myRbRearrangePackage.setText(rearrangeDescription); + myRbRearrangePackage.setVisible(myRearrangePackagesEnabled); final String moveDirectoryDescription; if (myDirectories.length > 1) { - moveDirectoryDescription = "Move " + myDirectories.length + " directories to another directory"; + moveDirectoryDescription = "Move everything from " + myDirectories.length + " directories to another directory"; } else { - moveDirectoryDescription = "Move directory " + myDirectories[0].getVirtualFile().getPresentableUrl() + " to another directory"; + moveDirectoryDescription = "Move everything from " + myDirectories[0].getVirtualFile().getPresentableUrl() + " to another directory"; } myRbMoveDirectory = new JRadioButton(); myRbMoveDirectory.setMnemonic('d'); diff --git a/java/java-impl/src/com/intellij/refactoring/rename/DirectoryAsPackageRenameHandler.java b/java/java-impl/src/com/intellij/refactoring/rename/DirectoryAsPackageRenameHandler.java index ac3aa984c0b3..d8c69e078e04 100644 --- a/java/java-impl/src/com/intellij/refactoring/rename/DirectoryAsPackageRenameHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/rename/DirectoryAsPackageRenameHandler.java @@ -22,11 +22,17 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.lang.TitledHandler; +import com.intellij.refactoring.move.moveClassesOrPackages.MoveDirectoryWithClassesProcessor; import org.jetbrains.annotations.NotNull; /** @@ -37,7 +43,7 @@ public class DirectoryAsPackageRenameHandler implements RenameHandler, TitledHan public boolean isAvailableOnDataContext(final DataContext dataContext) { final PsiElement element = PsiElementRenameHandler.getElement(dataContext); - return element instanceof PsiDirectory; + return element instanceof PsiDirectory && ProjectRootManager.getInstance(element.getProject()).getFileIndex().isInContent(((PsiDirectory)element).getVirtualFile()); } public boolean isRenaming(final DataContext dataContext) { @@ -59,9 +65,9 @@ public class DirectoryAsPackageRenameHandler implements RenameHandler, TitledHan doRename(element, project, element, editor); } - public static void doRename(PsiElement element, Project project, PsiElement nameSuggestionContext, Editor editor) { - PsiDirectory psiDirectory = (PsiDirectory)element; - PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(psiDirectory); + public static void doRename(PsiElement element, final Project project, PsiElement nameSuggestionContext, Editor editor) { + final PsiDirectory psiDirectory = (PsiDirectory)element; + final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(psiDirectory); final String qualifiedName = aPackage != null ? aPackage.getQualifiedName() : ""; if (aPackage == null || qualifiedName.length() == 0/*default package*/ || !JavaPsiFacade.getInstance(project).getNameHelper().isIdentifier(psiDirectory.getName())) { @@ -74,25 +80,87 @@ public class DirectoryAsPackageRenameHandler implements RenameHandler, TitledHan PsiElementRenameHandler.rename(aPackage, project, nameSuggestionContext, editor); } else { // the directory corresponds to a package that has multiple associated directories - StringBuffer message = new StringBuffer(); - RenameUtil.buildPackagePrefixChangedMessage(virtualFiles, message, qualifiedName); - buildMultipleDirectoriesInPackageMessage(message, aPackage, directories); - message.append(RefactoringBundle.message("directories.and.all.references.to.package.will.be.renamed", psiDirectory.getVirtualFile().getPresentableUrl())); - int ret = - Messages.showDialog(project, message.toString(), RefactoringBundle.message("warning.title"), - new String[]{ - RefactoringBundle.message("rename.package.button.text"), - RefactoringBundle.message("rename.directory.button.text"), - CommonBundle.getCancelButtonText()}, 0, Messages.getWarningIcon()); - if (ret == 0) { - PsiElementRenameHandler.rename(aPackage, project, nameSuggestionContext, editor); - } else if (ret == 1){ - PsiElementRenameHandler.rename(psiDirectory, project, nameSuggestionContext, editor); + final ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex(); + boolean inLib = false; + for (PsiDirectory directory : directories) { + inLib |= !projectFileIndex.isInContent(directory.getVirtualFile()); + } + + final PsiDirectory[] projectDirectories = aPackage.getDirectories(GlobalSearchScope.projectScope(project)); + if (inLib) { + final String promptMessage = "Package \'" + + aPackage.getName() + + "\' contains directories in libraries which cannot be renamed. Do you want to rename current directory"; + if (projectDirectories.length > 0) { + int ret = Messages + .showDialog(project, promptMessage + " or all directories in project?", RefactoringBundle.message("warning.title"), + new String[]{RefactoringBundle.message("rename.current.directory"), + RefactoringBundle.message("rename.directories"), CommonBundle.getCancelButtonText()}, 0, + Messages.getWarningIcon()); + renameDirs(project, nameSuggestionContext, editor, psiDirectory, aPackage, ret == 0 ? new PsiDirectory[] {psiDirectory} : projectDirectories); + } + else { + if (Messages.showDialog(project, promptMessage + "?", RefactoringBundle.message("warning.title"), + new String[]{CommonBundle.getOkButtonText(), CommonBundle.getCancelButtonText()}, 0, + Messages.getWarningIcon()) == DialogWrapper.OK_EXIT_CODE) { + renameDirs(project, nameSuggestionContext, editor, psiDirectory, aPackage, psiDirectory); + } + } + } + else { + final StringBuffer message = new StringBuffer(); + RenameUtil.buildPackagePrefixChangedMessage(virtualFiles, message, qualifiedName); + buildMultipleDirectoriesInPackageMessage(message, aPackage, directories); + message.append(RefactoringBundle.message("directories.and.all.references.to.package.will.be.renamed", + psiDirectory.getVirtualFile().getPresentableUrl())); + int ret = Messages.showDialog(project, message.toString(), RefactoringBundle.message("warning.title"), + new String[]{RefactoringBundle.message("rename.package.button.text"), + RefactoringBundle.message("rename.directory.button.text"), CommonBundle.getCancelButtonText()}, 0, + Messages.getWarningIcon()); + if (ret == 0) { + PsiElementRenameHandler.rename(aPackage, project, nameSuggestionContext, editor); + } + else if (ret == 1) { + renameDirs(project, nameSuggestionContext, editor, psiDirectory, aPackage, psiDirectory); + } } } } } + private static void renameDirs(final Project project, + final PsiElement nameSuggestionContext, + final Editor editor, + final PsiDirectory contextDirectory, + final PsiPackage aPackage, + final PsiDirectory... dirsToRename) { + final RenameDialog dialog = new RenameDialog(project, contextDirectory, nameSuggestionContext, editor) { + @Override + protected void doAction() { + final String newName = StringUtil.getQualifiedName(StringUtil.getPackageName(aPackage.getQualifiedName()), getNewName()); + final MoveDirectoryWithClassesProcessor moveProcessor = + new MoveDirectoryWithClassesProcessor(project, dirsToRename, null, isSearchInComments(), isSearchInNonJavaFiles(), false, null) { + @Override + public TargetDirectoryWrapper getTargetDirectory(final PsiDirectory dir) { + return new TargetDirectoryWrapper(dir.getParentDirectory(), getNewName()); + } + + @Override + protected String getTargetName() { + return newName; + } + + @Override + protected String getCommandName() { + return dirsToRename.length == 1 ? "Rename directory" : "Rename directories"; + } + }; + invokeRefactoring(moveProcessor); + } + }; + dialog.show(); + } + public static void buildMultipleDirectoriesInPackageMessage(StringBuffer message, PsiPackage aPackage, PsiDirectory[] directories) { diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src1/pack2/UsagesFromBoth.java b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src1/pack2/UsagesFromBoth.java new file mode 100644 index 000000000000..6d4410fd6ae6 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src1/pack2/UsagesFromBoth.java @@ -0,0 +1,9 @@ +package pack2; + +import pack1.*; +import target.pack1.S1; + +public class UsagesFromBoth { + S1 s1; + S2 s2; +} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src1/target/empty.txt b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src1/target/empty.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src1/target/pack1/S1.java b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src1/target/pack1/S1.java new file mode 100644 index 000000000000..bafd60dc4fa1 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src1/target/pack1/S1.java @@ -0,0 +1,3 @@ +package target.pack1; + +public class S1{} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src2/pack1/S2.java b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src2/pack1/S2.java new file mode 100644 index 000000000000..0bd2d2024dc6 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src2/pack1/S2.java @@ -0,0 +1,3 @@ +package pack1; + +public class S2{} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src2/pack2/UsagesFromBoth2.java b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src2/pack2/UsagesFromBoth2.java new file mode 100644 index 000000000000..9b53f3854379 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/after/src2/pack2/UsagesFromBoth2.java @@ -0,0 +1,9 @@ +package pack2; + +import pack1.*; +import target.pack1.S1; + +public class UsagesFromBoth2 { + S1 s1; + S2 s2; +} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src1/pack1/S1.java b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src1/pack1/S1.java new file mode 100644 index 000000000000..c0a5f31dbfba --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src1/pack1/S1.java @@ -0,0 +1,3 @@ +package pack1; + +public class S1{} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src1/pack2/UsagesFromBoth.java b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src1/pack2/UsagesFromBoth.java new file mode 100644 index 000000000000..906b1fe953d6 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src1/pack2/UsagesFromBoth.java @@ -0,0 +1,8 @@ +package pack2; + +import pack1.*; + +public class UsagesFromBoth { + S1 s1; + S2 s2; +} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src1/target/empty.txt b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src1/target/empty.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src2/pack1/S2.java b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src2/pack1/S2.java new file mode 100644 index 000000000000..0bd2d2024dc6 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src2/pack1/S2.java @@ -0,0 +1,3 @@ +package pack1; + +public class S2{} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src2/pack2/UsagesFromBoth2.java b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src2/pack2/UsagesFromBoth2.java new file mode 100644 index 000000000000..d8ece1cab742 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/movePackage/before/src2/pack2/UsagesFromBoth2.java @@ -0,0 +1,8 @@ +package pack2; + +import pack1.*; + +public class UsagesFromBoth2 { + S1 s1; + S2 s2; +} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/pack2/UsagesFromBoth.java b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/pack2/UsagesFromBoth.java new file mode 100644 index 000000000000..e21505a4f182 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/pack2/UsagesFromBoth.java @@ -0,0 +1,11 @@ +package pack2; + +import pack1.*; +import target.pack1.S1; +import target.pack1.subpack.SS; + +public class UsagesFromBoth { + S1 s1; + S2 s2; + SS ss; +} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/target/empty.txt b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/target/empty.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/target/pack1/S1.java b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/target/pack1/S1.java new file mode 100644 index 000000000000..bafd60dc4fa1 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/target/pack1/S1.java @@ -0,0 +1,3 @@ +package target.pack1; + +public class S1{} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/target/pack1/subpack/SS.java b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/target/pack1/subpack/SS.java new file mode 100644 index 000000000000..39cf0b625314 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src1/target/pack1/subpack/SS.java @@ -0,0 +1,3 @@ +package target.pack1.subpack; + +public class SS {} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src2/pack1/S2.java b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src2/pack1/S2.java new file mode 100644 index 000000000000..0bd2d2024dc6 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src2/pack1/S2.java @@ -0,0 +1,3 @@ +package pack1; + +public class S2{} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src2/pack2/UsagesFromBoth2.java b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src2/pack2/UsagesFromBoth2.java new file mode 100644 index 000000000000..9b53f3854379 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/after/src2/pack2/UsagesFromBoth2.java @@ -0,0 +1,9 @@ +package pack2; + +import pack1.*; +import target.pack1.S1; + +public class UsagesFromBoth2 { + S1 s1; + S2 s2; +} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/pack1/S1.java b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/pack1/S1.java new file mode 100644 index 000000000000..c0a5f31dbfba --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/pack1/S1.java @@ -0,0 +1,3 @@ +package pack1; + +public class S1{} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/pack1/subpack/SS.java b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/pack1/subpack/SS.java new file mode 100644 index 000000000000..869423cf5146 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/pack1/subpack/SS.java @@ -0,0 +1,3 @@ +package pack1.subpack; + +public class SS {} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/pack2/UsagesFromBoth.java b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/pack2/UsagesFromBoth.java new file mode 100644 index 000000000000..b0f9a94641d2 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/pack2/UsagesFromBoth.java @@ -0,0 +1,10 @@ +package pack2; + +import pack1.*; +import pack1.subpack.*; + +public class UsagesFromBoth { + S1 s1; + S2 s2; + SS ss; +} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/target/empty.txt b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src1/target/empty.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src2/pack1/S2.java b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src2/pack1/S2.java new file mode 100644 index 000000000000..0bd2d2024dc6 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src2/pack1/S2.java @@ -0,0 +1,3 @@ +package pack1; + +public class S2{} diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src2/pack2/UsagesFromBoth2.java b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src2/pack2/UsagesFromBoth2.java new file mode 100644 index 000000000000..d8ece1cab742 --- /dev/null +++ b/java/java-tests/testData/refactoring/movePackageAsDir/removeUnresolvedImports/before/src2/pack2/UsagesFromBoth2.java @@ -0,0 +1,8 @@ +package pack2; + +import pack1.*; + +public class UsagesFromBoth2 { + S1 s1; + S2 s2; +} diff --git a/java/java-tests/testSrc/com/intellij/refactoring/MovePackageAsDirectoryTest.java b/java/java-tests/testSrc/com/intellij/refactoring/MovePackageAsDirectoryTest.java new file mode 100644 index 000000000000..fdd9074160d6 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/refactoring/MovePackageAsDirectoryTest.java @@ -0,0 +1,82 @@ + +package com.intellij.refactoring; + +import com.intellij.JavaTestUtil; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.roots.ContentEntry; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiPackage; +import com.intellij.refactoring.move.moveClassesOrPackages.MoveDirectoryWithClassesProcessor; + +import java.util.Arrays; +import java.util.Comparator; + +public class MovePackageAsDirectoryTest extends MultiFileTestCase { + + @Override + protected String getTestDataPath() { + return JavaTestUtil.getJavaTestDataPath(); + } + + protected String getTestRoot() { + return "/refactoring/movePackageAsDir/"; + } + + public void testMovePackage() throws Exception { + doTest(createAction("pack1", "target")); + } + + public void testRemoveUnresolvedImports() throws Exception { + doTest(createAction("pack1", "target")); + } + + private PerformAction createAction(final String packageName, final String targetPackageName) { + return new PerformAction() { + public void performAction(VirtualFile rootDir, VirtualFile rootAfter) throws Exception { + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(myProject); + final Comparator directoryComparator = new Comparator() { + public int compare(PsiDirectory o1, PsiDirectory o2) { + return o1.getVirtualFile().getPresentableUrl().compareTo(o2.getVirtualFile().getPresentableUrl()); + } + }; + + final PsiPackage sourcePackage = psiFacade.findPackage(packageName); + assertNotNull(sourcePackage); + final PsiDirectory[] srcDirectories = sourcePackage.getDirectories(); + assertEquals(srcDirectories.length, 2); + Arrays.sort(srcDirectories, directoryComparator); + + final PsiPackage targetPackage = psiFacade.findPackage(targetPackageName); + assertNotNull(targetPackage); + final PsiDirectory[] targetDirectories = targetPackage.getDirectories(); + Arrays.sort(targetDirectories, directoryComparator); + assertTrue(targetDirectories.length > 0); + + new MoveDirectoryWithClassesProcessor(getProject(), new PsiDirectory[]{srcDirectories[0]}, targetDirectories[0], false, false, true, null).run(); + FileDocumentManager.getInstance().saveAllDocuments(); + } + }; + } + + protected void setupProject(VirtualFile rootDir) { + final ModifiableRootModel rootModel = ModuleRootManager.getInstance(myModule).getModifiableModel(); + final ContentEntry contentEntry = rootModel.addContentEntry(rootDir); + final VirtualFile[] children = rootDir.getChildren(); + for (VirtualFile child : children) { + if (child.getName().startsWith("src")) { + contentEntry.addSourceFolder(child, false); + } + } + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + rootModel.commit(); + } + }); + } +} diff --git a/platform/platform-resources-en/src/messages/RefactoringBundle.properties b/platform/platform-resources-en/src/messages/RefactoringBundle.properties index 25abb7ea4dca..64254c508fa9 100644 --- a/platform/platform-resources-en/src/messages/RefactoringBundle.properties +++ b/platform/platform-resources-en/src/messages/RefactoringBundle.properties @@ -219,6 +219,8 @@ multiple.directories.correspond.to.package=Multiple directories correspond to pa directories.and.all.references.to.package.will.be.renamed=\n\nDo you want to rename the whole package or directory \n{0} only? rename.package.button.text=Rename &package rename.directory.button.text=Rename &directory +rename.directories= Rename &all +rename.current.directory= Rename ¤t warning.title=Warning replace.constructor.with.factory.method.title=Replace Constructor With Factory Method class.does.not.have.implicit.default.consructor=Class {0} does not have implicit default consructor.