Java tutorial
/* * Copyright 2000-2011 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.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.consulo.java.platform.util.JavaProjectRootsUtil; 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 PsiJavaPackage) 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 JspClass) return true; } parentFile = (PsiFile) element; } else { //if (element instanceof JspClass) 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.isOutsideSourceRoot(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)) { new MoveClassesOrPackagesToNewDirectoryDialog((PsiDirectory) targetContainer, adjustedElements, callback).show(); return; } } } MoveClassesOrPackagesImpl.doMove(project, adjustedElements, targetContainer, callback); } private static void moveDirectoriesLibrariesSafe(Project project, PsiElement targetContainer, MoveCallback callback, PsiDirectory[] directories, String prompt) { final PsiJavaPackage 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 == 0) { moveAsDirectory(project, targetContainer, callback, directories); } else if (ret == 1) { moveAsDirectory(project, targetContainer, callback, projectDirectories); } } else if (Messages.showOkCancelDialog(project, prompt + "?", RefactoringBundle.message("warning.title"), Messages.getWarningIcon()) == DialogWrapper.OK_EXIT_CODE) { moveAsDirectory(project, targetContainer, callback, directories); } } 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, PsiJavaPackage 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 PsiJavaPackage) { final Module module = LangDataKeys.TARGET_MODULE.getData(dataContext); if (module != null) { final PsiDirectory[] directories = ((PsiJavaPackage) 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 PsiJavaPackage 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 PsiJavaPackage 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 (ProjectRootManager.getInstance(project).getContentSourceRoots().length == 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 PsiJavaPackage 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; } MoveClassesOrPackagesImpl.doMove(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 PsiJavaPackage aPackage = JavaDirectoryServiceImpl.getInstance() .getPackage((PsiDirectory) source); if (aPackage != null && !MoveClassesOrPackagesImpl.checkNesting(target.getProject(), aPackage, target, false)) return true; } return super.isMoveRedundant(source, target); } }