| /* |
| * Copyright 2000-2013 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.refactoring.move.moveClassesOrPackages; |
| |
| import com.intellij.CommonBundle; |
| import com.intellij.openapi.actionSystem.DataContext; |
| import com.intellij.openapi.actionSystem.LangDataKeys; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleUtilCore; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.roots.JavaProjectRootsUtil; |
| 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.vfs.VirtualFile; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.file.JavaDirectoryServiceImpl; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.refactoring.JavaRefactoringSettings; |
| import com.intellij.refactoring.RefactoringBundle; |
| import com.intellij.refactoring.move.MoveCallback; |
| import com.intellij.refactoring.move.MoveHandlerDelegate; |
| import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil; |
| import com.intellij.refactoring.rename.JavaVetoRenameCondition; |
| import com.intellij.refactoring.util.CommonRefactoringUtil; |
| import com.intellij.refactoring.util.RadioUpDownListener; |
| import com.intellij.refactoring.util.RefactoringUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.containers.HashSet; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.util.Arrays; |
| |
| public class JavaMoveClassesOrPackagesHandler extends MoveHandlerDelegate { |
| private static final Logger LOG = Logger.getInstance("#" + JavaMoveClassesOrPackagesHandler.class.getName()); |
| private static final JavaVetoRenameCondition VETO_RENAME_CONDITION = new JavaVetoRenameCondition(); |
| |
| public static boolean isPackageOrDirectory(final PsiElement element) { |
| if (element instanceof PsiPackage) return true; |
| return element instanceof PsiDirectory && JavaDirectoryService.getInstance().getPackage((PsiDirectory)element) != null; |
| } |
| |
| public static boolean isReferenceInAnonymousClass(@Nullable final PsiReference reference) { |
| if (reference instanceof PsiJavaCodeReferenceElement && |
| ((PsiJavaCodeReferenceElement)reference).getParent() instanceof PsiAnonymousClass) { |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean canMove(PsiElement[] elements, @Nullable PsiElement targetContainer) { |
| for (PsiElement element : elements) { |
| if (!isPackageOrDirectory(element) && invalid4Move(element)) return false; |
| } |
| return super.canMove(elements, targetContainer); |
| } |
| |
| public static boolean invalid4Move(PsiElement element) { |
| PsiFile parentFile; |
| if (element instanceof PsiClassOwner) { |
| final PsiClass[] classes = ((PsiClassOwner)element).getClasses(); |
| if (classes.length == 0) return true; |
| for (PsiClass aClass : classes) { |
| if (aClass instanceof PsiSyntheticClass) return true; |
| } |
| parentFile = (PsiFile)element; |
| } |
| else { |
| if (element instanceof PsiSyntheticClass) return true; |
| if (!(element instanceof PsiClass)) return true; |
| if (element instanceof PsiAnonymousClass) return true; |
| if (((PsiClass)element).getContainingClass() != null) return true; |
| parentFile = element.getContainingFile(); |
| } |
| return parentFile instanceof PsiJavaFile && JavaProjectRootsUtil.isOutsideJavaSourceRoot(parentFile); |
| } |
| |
| @Override |
| public boolean isValidTarget(PsiElement psiElement, PsiElement[] sources) { |
| if (isPackageOrDirectory(psiElement)) return true; |
| boolean areAllClasses = true; |
| for (PsiElement source : sources) { |
| areAllClasses &= !isPackageOrDirectory(source) && !invalid4Move(source); |
| } |
| return areAllClasses && psiElement instanceof PsiClass; |
| } |
| |
| public PsiElement[] adjustForMove(final Project project, final PsiElement[] sourceElements, final PsiElement targetElement) { |
| return MoveClassesOrPackagesImpl.adjustForMove(project,sourceElements, targetElement); |
| } |
| |
| public void doMove(final Project project, final PsiElement[] elements, final PsiElement targetContainer, final MoveCallback callback) { |
| final PsiDirectory[] directories = new PsiDirectory[elements.length]; |
| final String prompt = getPromptToMoveDirectoryLibrariesSafe(elements); |
| if (prompt != null) { |
| System.arraycopy(elements, 0, directories, 0, directories.length); |
| moveDirectoriesLibrariesSafe(project, targetContainer, callback, directories, prompt); |
| return; |
| } |
| if (canMoveOrRearrangePackages(elements) ) { |
| System.arraycopy(elements, 0, directories, 0, directories.length); |
| SelectMoveOrRearrangePackageDialog dialog = new SelectMoveOrRearrangePackageDialog(project, directories, targetContainer == null); |
| dialog.show(); |
| if (!dialog.isOK()) return; |
| |
| if (dialog.isPackageRearrageSelected()) { |
| MoveClassesOrPackagesImpl.doRearrangePackage(project, directories); |
| return; |
| } |
| |
| if (dialog.isMoveDirectory()) { |
| moveAsDirectory(project, targetContainer, callback, directories); |
| return; |
| } |
| } |
| final PsiElement[] adjustedElements = MoveClassesOrPackagesImpl.adjustForMove(project, elements, targetContainer); |
| if (adjustedElements == null) return; |
| |
| if (targetContainer instanceof PsiDirectory) { |
| if (CommonRefactoringUtil.checkReadOnlyStatusRecursively(project, Arrays.asList(adjustedElements), true)) { |
| if (!packageHasMultipleDirectoriesInModule(project, (PsiDirectory)targetContainer)) { |
| createMoveClassesOrPackagesToNewDirectoryDialog((PsiDirectory)targetContainer, adjustedElements, callback).show(); |
| return; |
| } |
| } |
| } |
| doMoveWithMoveClassesDialog(project, adjustedElements, targetContainer, callback); |
| } |
| |
| protected void doMoveWithMoveClassesDialog(final Project project, |
| PsiElement[] adjustedElements, |
| PsiElement initialTargetElement, |
| final MoveCallback moveCallback) { |
| MoveClassesOrPackagesImpl.doMove(project, adjustedElements, initialTargetElement, moveCallback); |
| } |
| |
| private static void moveDirectoriesLibrariesSafe(Project project, |
| PsiElement targetContainer, |
| MoveCallback callback, |
| PsiDirectory[] directories, |
| String prompt) { |
| final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(directories[0]); |
| LOG.assertTrue(aPackage != null); |
| final PsiDirectory[] projectDirectories = aPackage.getDirectories(GlobalSearchScope.projectScope(project)); |
| if (projectDirectories.length > 1) { |
| int ret = Messages |
| .showYesNoCancelDialog(project, prompt + " or all directories in project?", RefactoringBundle.message("warning.title"), |
| RefactoringBundle.message("move.current.directory"), |
| RefactoringBundle.message("move.directories"), |
| CommonBundle.getCancelButtonText(), Messages.getWarningIcon()); |
| if (ret == Messages.YES) { |
| moveAsDirectory(project, targetContainer, callback, directories); |
| } |
| else if (ret == Messages.NO) { |
| moveAsDirectory(project, targetContainer, callback, projectDirectories); |
| } |
| } |
| else if (Messages.showOkCancelDialog(project, prompt + "?", RefactoringBundle.message("warning.title"), |
| Messages.getWarningIcon()) == Messages.OK) { |
| moveAsDirectory(project, targetContainer, callback, directories); |
| } |
| } |
| |
| @NotNull |
| protected DialogWrapper createMoveClassesOrPackagesToNewDirectoryDialog(@NotNull final PsiDirectory directory, |
| PsiElement[] elementsToMove, |
| final MoveCallback moveCallback) { |
| return new MoveClassesOrPackagesToNewDirectoryDialog(directory, elementsToMove, moveCallback); |
| } |
| |
| private static void moveAsDirectory(Project project, |
| PsiElement targetContainer, |
| final MoveCallback callback, |
| final PsiDirectory[] directories) { |
| 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.setPrepareSuccessfulSwingThreadCallback(new Runnable() { |
| @Override |
| public void run() { |
| } |
| }); |
| processor.run(); |
| } |
| else { |
| final boolean containsJava = hasJavaFiles(directories[0]); |
| if (!containsJava) { |
| MoveFilesOrDirectoriesUtil.doMove(project, new PsiElement[]{directories[0]}, new PsiElement[]{targetContainer}, callback); |
| return; |
| } |
| final MoveClassesOrPackagesToNewDirectoryDialog dlg = |
| new MoveClassesOrPackagesToNewDirectoryDialog(directories[0], new PsiElement[0], false, callback) { |
| @Override |
| protected void performRefactoring(Project project, |
| final PsiDirectory targetDirectory, |
| PsiPackage aPackage, |
| boolean searchInComments, |
| boolean searchForTextOccurences) { |
| try { |
| for (PsiDirectory dir: directories) { |
| MoveFilesOrDirectoriesUtil.checkIfMoveIntoSelf(dir, targetDirectory); |
| } |
| } |
| catch (IncorrectOperationException e) { |
| Messages.showErrorDialog(project, e.getMessage(), RefactoringBundle.message("cannot.move")); |
| return; |
| } |
| final MoveDirectoryWithClassesProcessor processor = |
| new MoveDirectoryWithClassesProcessor(project, directories, targetDirectory, searchInComments, searchForTextOccurences, |
| true, callback); |
| processor.setPrepareSuccessfulSwingThreadCallback(new Runnable() { |
| @Override |
| public void run() { |
| } |
| }); |
| processor.run(); |
| } |
| }; |
| dlg.show(); |
| } |
| } |
| |
| public static boolean hasJavaFiles(PsiDirectory directory) { |
| final boolean [] containsJava = new boolean[]{false}; |
| directory.accept(new JavaRecursiveElementWalkingVisitor() { |
| @Override |
| public void visitElement(PsiElement element) { |
| if (containsJava[0]) return; |
| if (element instanceof PsiDirectory) { |
| super.visitElement(element); |
| } |
| } |
| |
| @Override |
| public void visitFile(PsiFile file) { |
| containsJava[0] = file instanceof PsiJavaFile; |
| } |
| }); |
| return containsJava[0]; |
| } |
| |
| @Override |
| public PsiElement adjustTargetForMove(DataContext dataContext, PsiElement targetContainer) { |
| if (targetContainer instanceof PsiPackage) { |
| final Module module = LangDataKeys.TARGET_MODULE.getData(dataContext); |
| if (module != null) { |
| final PsiDirectory[] directories = ((PsiPackage)targetContainer).getDirectories(GlobalSearchScope.moduleScope(module)); |
| if (directories.length == 1) { |
| return directories[0]; |
| } |
| } |
| } |
| return super.adjustTargetForMove(dataContext, targetContainer); |
| } |
| |
| public static boolean packageHasMultipleDirectoriesInModule(Project project, PsiDirectory targetElement) { |
| final PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage(targetElement); |
| if (psiPackage != null) { |
| final Module module = ModuleUtilCore.findModuleForFile(targetElement.getVirtualFile(), project); |
| if (module != null) { |
| if (psiPackage.getDirectories(GlobalSearchScope.moduleScope(module)).length > 1) return true; |
| } |
| } |
| return false; |
| } |
| |
| @Nullable |
| private static String getPromptToMoveDirectoryLibrariesSafe(PsiElement[] elements) { |
| if (elements.length == 0 || elements.length > 1) return null; |
| final Project project = elements[0].getProject(); |
| final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); |
| if (!(elements[0] instanceof PsiDirectory)) return null; |
| final PsiDirectory directory = ((PsiDirectory)elements[0]); |
| if (RefactoringUtil.isSourceRoot(directory)) { |
| return null; |
| } |
| final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(directory); |
| if (aPackage == null) return null; |
| if ("".equals(aPackage.getQualifiedName())) return null; |
| final PsiDirectory[] directories = aPackage.getDirectories(); |
| |
| boolean inLib = false; |
| for (PsiDirectory psiDirectory : directories) { |
| inLib |= !fileIndex.isInContent(psiDirectory.getVirtualFile()); |
| } |
| |
| return inLib ? "Package \'" + |
| aPackage.getName() + |
| "\' contains directories in libraries which cannot be moved. Do you want to move current directory" : null; |
| } |
| |
| private static boolean canMoveOrRearrangePackages(PsiElement[] elements) { |
| if (elements.length == 0) return false; |
| final Project project = elements[0].getProject(); |
| if (JavaProjectRootsUtil.getSuitableDestinationSourceRoots(project).size() == 1) { |
| return false; |
| } |
| for (PsiElement element : elements) { |
| if (!(element instanceof PsiDirectory)) return false; |
| final PsiDirectory directory = ((PsiDirectory)element); |
| if (RefactoringUtil.isSourceRoot(directory)) { |
| return false; |
| } |
| final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(directory); |
| if (aPackage == null) return false; |
| if (aPackage.getQualifiedName().isEmpty()) return false; |
| final VirtualFile sourceRootForFile = ProjectRootManager.getInstance(element.getProject()).getFileIndex() |
| .getSourceRootForFile(directory.getVirtualFile()); |
| if (sourceRootForFile == null) return false; |
| } |
| return true; |
| } |
| |
| public static boolean hasPackages(PsiDirectory directory) { |
| if (JavaDirectoryService.getInstance().getPackage(directory) != null) { |
| return true; |
| } |
| return false; |
| } |
| |
| private static class SelectMoveOrRearrangePackageDialog extends DialogWrapper { |
| private JRadioButton myRbMovePackage; |
| private JRadioButton myRbRearrangePackage; |
| 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(); |
| } |
| |
| protected JComponent createNorthPanel() { |
| return new JLabel(RefactoringBundle.message("what.would.you.like.to.do")); |
| } |
| |
| public JComponent getPreferredFocusedComponent() { |
| return myRbMovePackage; |
| } |
| |
| protected String getDimensionServiceKey() { |
| return "#com.intellij.refactoring.move.MoveHandler.SelectRefactoringDialog"; |
| } |
| |
| |
| protected JComponent createCenterPanel() { |
| JPanel panel = new JPanel(new BorderLayout()); |
| |
| |
| final HashSet<String> packages = new HashSet<String>(); |
| for (PsiDirectory directory : myDirectories) { |
| packages.add(JavaDirectoryService.getInstance().getPackage(directory).getQualifiedName()); |
| } |
| final String moveDescription; |
| LOG.assertTrue(myDirectories.length > 0); |
| LOG.assertTrue(packages.size() > 0); |
| if (packages.size() > 1) { |
| moveDescription = RefactoringBundle.message("move.packages.to.another.package", packages.size()); |
| } |
| else { |
| final String qName = packages.iterator().next(); |
| moveDescription = RefactoringBundle.message("move.package.to.another.package", qName); |
| } |
| |
| myRbMovePackage = new JRadioButton(); |
| myRbMovePackage.setText(moveDescription); |
| myRbMovePackage.setSelected(true); |
| |
| final String rearrangeDescription; |
| if (myDirectories.length > 1) { |
| rearrangeDescription = RefactoringBundle.message("move.directories.to.another.source.root", myDirectories.length); |
| } |
| else { |
| rearrangeDescription = RefactoringBundle.message("move.directory.to.another.source.root", myDirectories[0].getVirtualFile().getPresentableUrl()); |
| } |
| myRbRearrangePackage = new JRadioButton(); |
| myRbRearrangePackage.setText(rearrangeDescription); |
| myRbRearrangePackage.setVisible(myRearrangePackagesEnabled); |
| |
| final String moveDirectoryDescription; |
| if (myDirectories.length > 1) { |
| moveDirectoryDescription = "Move everything from " + myDirectories.length + " directories to another directory"; |
| } |
| else { |
| moveDirectoryDescription = "Move everything from " + myDirectories[0].getVirtualFile().getPresentableUrl() + " to another directory"; |
| } |
| myRbMoveDirectory = new JRadioButton(); |
| myRbMoveDirectory.setMnemonic('e'); |
| myRbMoveDirectory.setText(moveDirectoryDescription); |
| |
| ButtonGroup gr = new ButtonGroup(); |
| gr.add(myRbMovePackage); |
| gr.add(myRbRearrangePackage); |
| gr.add(myRbMoveDirectory); |
| |
| if (myRearrangePackagesEnabled) { |
| new RadioUpDownListener(myRbMovePackage, myRbRearrangePackage, myRbMoveDirectory); |
| } else { |
| new RadioUpDownListener(myRbMovePackage, myRbMoveDirectory); |
| } |
| |
| Box box = Box.createVerticalBox(); |
| box.add(Box.createVerticalStrut(5)); |
| box.add(myRbMovePackage); |
| box.add(myRbRearrangePackage); |
| box.add(myRbMoveDirectory); |
| panel.add(box, BorderLayout.CENTER); |
| return panel; |
| } |
| |
| public boolean isPackageRearrageSelected() { |
| return myRbRearrangePackage.isSelected(); |
| } |
| |
| public boolean isMoveDirectory() { |
| return myRbMoveDirectory.isSelected(); |
| } |
| } |
| |
| |
| public boolean tryToMove(final PsiElement element, final Project project, final DataContext dataContext, final PsiReference reference, |
| final Editor editor) { |
| if (isPackageOrDirectory(element)) return false; |
| if (isReferenceInAnonymousClass(reference)) return false; |
| |
| if (!invalid4Move(element)) { |
| final PsiElement initialTargetElement = LangDataKeys.TARGET_PSI_ELEMENT.getData(dataContext); |
| PsiElement[] adjustedElements = adjustForMove(project, new PsiElement[]{element}, initialTargetElement); |
| if (adjustedElements == null) { |
| return true; |
| } |
| doMoveWithMoveClassesDialog(project, adjustedElements, initialTargetElement, null); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean isMoveRedundant(PsiElement source, PsiElement target) { |
| if (target instanceof PsiDirectory && source instanceof PsiClass) { |
| try { |
| JavaDirectoryServiceImpl.checkCreateClassOrInterface((PsiDirectory)target, ((PsiClass)source).getName()); |
| } |
| catch (IncorrectOperationException e) { |
| return true; |
| } |
| } |
| if (target instanceof PsiDirectory && source instanceof PsiDirectory) { |
| final PsiPackage aPackage = JavaDirectoryServiceImpl.getInstance().getPackage((PsiDirectory)source); |
| if (aPackage != null && !MoveClassesOrPackagesImpl.checkNesting(target.getProject(), aPackage, target, false)) return true; |
| } |
| return super.isMoveRedundant(source, target); |
| } |
| } |