diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddModuleDependencyFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddModuleDependencyFix.java new file mode 100644 index 000000000000..e9b58416d2b2 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddModuleDependencyFix.java @@ -0,0 +1,187 @@ +/* + * Copyright 2000-2012 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.codeInsight.daemon.impl.quickfix; + +import com.intellij.codeInsight.daemon.QuickFixBundle; +import com.intellij.codeInsight.daemon.impl.actions.AddImportAction; +import com.intellij.compiler.ModuleCompilerUtil; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleType; +import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.*; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; +import com.intellij.ui.ListCellRendererWrapper; +import com.intellij.ui.components.JBList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +/** + * User: anna + * Date: 11/20/12 + */ +class AddModuleDependencyFix extends OrderEntryFix { + private final List myModules = new ArrayList(); + private final Module myCurrentModule; + private final VirtualFile myClassVFile; + private final PsiClass[] myClasses; + private final PsiReference myReference; + private static final Logger LOG = Logger.getInstance("#" + AddModuleDependencyFix.class.getName()); + + public AddModuleDependencyFix(Module currentModule, + VirtualFile classVFile, + PsiClass[] classes, + PsiReference reference) { + final PsiElement psiElement = reference.getElement(); + final Project project = psiElement.getProject(); + final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); + final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); + + for (PsiClass aClass : classes) { + if (!facade.getResolveHelper().isAccessible(aClass, psiElement, aClass)) continue; + PsiFile psiFile = aClass.getContainingFile(); + if (psiFile == null) continue; + VirtualFile virtualFile = psiFile.getVirtualFile(); + if (virtualFile == null) continue; + final Module classModule = fileIndex.getModuleForFile(virtualFile); + if (classModule != null && classModule != currentModule && !ModuleRootManager.getInstance(currentModule).isDependsOn(classModule)) { + myModules.add(classModule); + } + } + myCurrentModule = currentModule; + myClassVFile = classVFile; + myClasses = classes; + myReference = reference; + } + + @Override + @NotNull + public String getText() { + return myModules.size() == 1 ? QuickFixBundle.message("orderEntry.fix.add.dependency.on.module", myModules.get(0).getName()) + : "Add dependency on module..."; + } + + @Override + @NotNull + public String getFamilyName() { + return QuickFixBundle.message("orderEntry.fix.family.add.module.dependency"); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + for (Module module : myModules) { + if (module.isDisposed()) return false; + } + return !project.isDisposed() && !myModules.isEmpty() && !myCurrentModule.isDisposed(); + } + + @Override + public void invoke(@NotNull final Project project, @Nullable final Editor editor, PsiFile file) { + if (myModules.size() == 1) { + addDependencyOnModule(project, editor, myModules.get(0)); + } + else { + final JBList list = new JBList(myModules); + list.setCellRenderer(new ListCellRendererWrapper() { + @Override + public void customize(JList list, Module module, int index, boolean selected, boolean hasFocus) { + if (module != null) { + setIcon(ModuleType.get(module).getIcon()); + setText(module.getName()); + } + } + }); + final JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list) + .setTitle("Choose Module to Add Dependency on") + .setMovable(false) + .setResizable(false) + .setRequestFocus(true) + .setItemChoosenCallback(new Runnable() { + public void run() { + final Object value = list.getSelectedValue(); + if (value instanceof Module) { + addDependencyOnModule(project, editor, (Module)value); + } + } + }).createPopup(); + if (editor != null) { + popup.showInBestPositionFor(editor); + } else { + popup.showCenteredInCurrentWindow(project); + } + } + } + + private void addDependencyOnModule(final Project project, final Editor editor, final Module module) { + final Runnable doit = new Runnable() { + @Override + public void run() { + final boolean test = ModuleRootManager.getInstance(myCurrentModule).getFileIndex().isInTestSourceContent(myClassVFile); + ModuleRootModificationUtil.addDependency(myCurrentModule, module, + test ? DependencyScope.TEST : DependencyScope.COMPILE, false); + if (editor != null) { + final List targetClasses = new ArrayList(); + for (PsiClass psiClass : myClasses) { + if (ModuleUtilCore.findModuleForPsiElement(psiClass) == module) { + targetClasses.add(psiClass); + } + } + new AddImportAction(project, myReference, editor, targetClasses.toArray(new PsiClass[targetClasses.size()])).execute(); + } + } + }; + final Pair circularModules = ModuleCompilerUtil.addingDependencyFormsCircularity(myCurrentModule, module); + if (circularModules == null) { + doit.run(); + } + else { + showCircularWarningAndContinue(project, circularModules, module, doit); + } + } + + + private static void showCircularWarningAndContinue(final Project project, final Pair circularModules, + final Module classModule, + final Runnable doit) { + final String message = QuickFixBundle.message("orderEntry.fix.circular.dependency.warning", classModule.getName(), + circularModules.getFirst().getName(), circularModules.getSecond().getName()); + if (ApplicationManager.getApplication().isUnitTestMode()) throw new RuntimeException(message); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (!project.isOpen()) return; + int ret = Messages.showOkCancelDialog(project, message, + QuickFixBundle.message("orderEntry.fix.title.circular.dependency.warning"), + Messages.getWarningIcon()); + if (ret == 0) { + ApplicationManager.getApplication().runWriteAction(doit); + } + } + }); + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MoveClassToModuleFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MoveClassToModuleFix.java index 11b49b2dc08f..25244265e76d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MoveClassToModuleFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MoveClassToModuleFix.java @@ -21,36 +21,156 @@ import com.intellij.codeInsight.hint.QuestionAction; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.ide.DataManager; import com.intellij.ide.util.PackageUtil; +import com.intellij.ide.util.PsiElementListCellRenderer; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.actionSystem.impl.SimpleDataContext; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.ui.popup.JBPopupFactory; +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.psi.search.PsiShortNamesCache; import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.refactoring.RefactoringActionHandlerFactory; +import com.intellij.ui.components.JBList; import com.intellij.util.IncorrectOperationException; +import org.codehaus.groovy.util.ListHashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; /** * @author cdr */ -public class MoveClassToModuleFix { - private MoveClassToModuleFix() { +public class MoveClassToModuleFix implements IntentionAction { + private final Map myModules = new ListHashMap(); + private final String myReferenceName; + private final Module myCurrentModule; + private final PsiDirectory mySourceRoot; + private static final Logger LOG = Logger.getInstance("#" + MoveClassToModuleFix.class.getName()); + + public MoveClassToModuleFix(String referenceName, Module currentModule, PsiDirectory root, PsiElement psiElement) { + myReferenceName = referenceName; + myCurrentModule = currentModule; + mySourceRoot = root; + final Project project = psiElement.getProject(); + final PsiClass[] classes = PsiShortNamesCache.getInstance(project).getClassesByName(referenceName, GlobalSearchScope.allScope(project)); + final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); + final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); + for (final PsiClass aClass : classes) { + if (!facade.getResolveHelper().isAccessible(aClass, psiElement, aClass)) continue; + final PsiFile psiFile = aClass.getContainingFile(); + if (!(psiFile instanceof PsiJavaFile)) continue; + if (aClass.getQualifiedName() == null) continue; + VirtualFile virtualFile = psiFile.getVirtualFile(); + if (virtualFile == null) continue; + final Module classModule = fileIndex.getModuleForFile(virtualFile); + if (classModule != null && classModule != currentModule && !ModuleRootManager.getInstance(currentModule).isDependsOn(classModule)) { + myModules.put(aClass, classModule); + } + } + } + + @Override + @NotNull + public String getText() { + if (myModules.size() == 1) { + final PsiClass aClass = myModules.keySet().iterator().next(); + return "Move '" + aClass.getQualifiedName() + "' from module '" + myModules.get(aClass).getName() + + "' to '" + myCurrentModule.getName() + "'"; + } + return "Move '" + myReferenceName + "' in '" + myCurrentModule.getName() + "'..."; + } + + @Override + @NotNull + public String getFamilyName() { + return "move it"; + } + + @Override + public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) { + return !myModules.isEmpty(); + } + + @Override + public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { + if (myModules.size() == 1) { + moveClass(project, editor, file, myModules.keySet().iterator().next()); + } + else { + LOG.assertTrue(editor != null); + final JBList list = new JBList(myModules.keySet()); + list.setCellRenderer(new PsiElementListCellRenderer() { + @Override + public String getElementText(PsiClass psiClass) { + return psiClass.getQualifiedName(); + } + + @Nullable + @Override + protected String getContainerText(PsiClass element, String name) { + return null; + } + + @Override + protected int getIconFlags() { + return 0; + } + }); + JBPopupFactory.getInstance().createListPopupBuilder(list) + .setTitle("Choose Class to Move") + .setMovable(false) + .setResizable(false) + .setRequestFocus(true) + .setItemChoosenCallback(new Runnable() { + public void run() { + final Object value = list.getSelectedValue(); + if (value instanceof PsiClass) { + moveClass(project, editor, file, (PsiClass)value); + } + } + }).createPopup().showInBestPositionFor(editor); + } + } + + private void moveClass(Project project, Editor editor, PsiFile file, PsiClass aClass) { + RefactoringActionHandler moveHandler = RefactoringActionHandlerFactory.getInstance().createMoveHandler(); + DataManager dataManager = DataManager.getInstance(); + DataContext dataContext = dataManager.getDataContext(); + final String fqName = aClass.getQualifiedName(); + LOG.assertTrue(fqName != null); + PsiDirectory directory = PackageUtil + .findOrCreateDirectoryForPackage(myCurrentModule, StringUtil.getPackageName(fqName), mySourceRoot, true); + DataContext context = SimpleDataContext.getSimpleContext(LangDataKeys.TARGET_PSI_ELEMENT.getName(), directory, dataContext); + + moveHandler.invoke(project, new PsiElement[]{aClass}, context); + PsiReference reference = file.findReferenceAt(editor.getCaretModel().getOffset()); + PsiClass newClass = JavaPsiFacade.getInstance(project).findClass(fqName, GlobalSearchScope.moduleScope(myCurrentModule)); + if (reference != null && newClass != null) { + final QuestionAction action = new AddImportAction(project, reference, editor, newClass); + action.execute(); + } + } + + @Override + public boolean startInWriteAction() { + return false; } public static void registerFixes(QuickFixActionRegistrar registrar, final PsiJavaCodeReferenceElement reference) { final PsiElement psiElement = reference.getElement(); @NonNls final String referenceName = reference.getRangeInElement().substring(psiElement.getText()); - Project project = psiElement.getProject(); final PsiFile containingFile = psiElement.getContainingFile(); if (containingFile == null) return; @@ -73,64 +193,6 @@ public class MoveClassToModuleFix { final PsiDirectory sourceRoot = PsiManager.getInstance(project).findDirectory(vsourceRoot); if (sourceRoot == null) return; - final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); - PsiClass[] classes = PsiShortNamesCache.getInstance(project).getClassesByName(referenceName, GlobalSearchScope.allScope(project)); - for (final PsiClass aClass : classes) { - if (!facade.getResolveHelper().isAccessible(aClass, psiElement, aClass)) continue; - final PsiFile psiFile = aClass.getContainingFile(); - if (!(psiFile instanceof PsiJavaFile)) continue; - PsiJavaFile javaFile = (PsiJavaFile)psiFile; - final String packageName = javaFile.getPackageName(); - VirtualFile virtualFile = psiFile.getVirtualFile(); - if (virtualFile == null) continue; - final Module classModule = fileIndex.getModuleForFile(virtualFile); - if (classModule != null && classModule != currentModule && !ModuleRootManager.getInstance(currentModule).isDependsOn(classModule)) { - IntentionAction action = new IntentionAction() { - @Override - @NotNull - public String getText() { - return "Move '"+aClass.getQualifiedName()+"' from module '" + classModule.getName() + - "' to '"+currentModule.getName()+"'"; - } - - @Override - @NotNull - public String getFamilyName() { - return "move it"; - } - - @Override - public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) { - return true; - } - - @Override - public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { - RefactoringActionHandler moveHandler = RefactoringActionHandlerFactory.getInstance().createMoveHandler(); - DataManager dataManager = DataManager.getInstance(); - DataContext dataContext = dataManager.getDataContext(); - PsiDirectory directory = PackageUtil.findOrCreateDirectoryForPackage(currentModule, packageName, sourceRoot, true); - DataContext context = SimpleDataContext.getSimpleContext(LangDataKeys.TARGET_PSI_ELEMENT.getName(), directory, dataContext); - String qualifiedName = aClass.getQualifiedName(); - if (qualifiedName == null) { - return; - } - moveHandler.invoke(project, new PsiElement[]{aClass}, context); - PsiReference reference = file.findReferenceAt(editor.getCaretModel().getOffset()); - PsiClass newClass = JavaPsiFacade.getInstance(project).findClass(qualifiedName, GlobalSearchScope.moduleScope(currentModule)); - if (reference != null && newClass != null) { - final QuestionAction action = new AddImportAction(project, reference, editor, newClass); - action.execute(); - } - } - - @Override - public boolean startInWriteAction() { - return false; - } - }; - registrar.register(action); - } - } + registrar.register(new MoveClassToModuleFix(referenceName, currentModule, sourceRoot, psiElement)); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/OrderEntryFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/OrderEntryFix.java index 5b003db9b801..7a867df6f1fd 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/OrderEntryFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/OrderEntryFix.java @@ -22,22 +22,17 @@ import com.intellij.codeInsight.daemon.impl.actions.AddImportAction; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.compiler.ModuleCompilerUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleUtil; -import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.ex.JavaSdkUtil; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.impl.OrderEntryUtil; import com.intellij.openapi.roots.libraries.Library; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -59,7 +54,7 @@ import java.util.*; * @author cdr */ public abstract class OrderEntryFix implements IntentionAction, LocalQuickFix { - private OrderEntryFix() { + OrderEntryFix() { } @Override @@ -180,63 +175,15 @@ public abstract class OrderEntryFix implements IntentionAction, LocalQuickFix { Set librariesToAdd = new THashSet(); final JavaPsiFacade facade = JavaPsiFacade.getInstance(psiElement.getProject()); final PsiClass[] classes = PsiShortNamesCache.getInstance(project).getClassesByName(referenceName, GlobalSearchScope.allScope(project)); + final OrderEntryFix moduleDependencyFix = new AddModuleDependencyFix(currentModule, classVFile, classes, reference); + registrar.register(moduleDependencyFix); + result.add(moduleDependencyFix); for (final PsiClass aClass : classes) { if (!facade.getResolveHelper().isAccessible(aClass, psiElement, aClass)) continue; PsiFile psiFile = aClass.getContainingFile(); if (psiFile == null) continue; VirtualFile virtualFile = psiFile.getVirtualFile(); if (virtualFile == null) continue; - final Module classModule = fileIndex.getModuleForFile(virtualFile); - if (classModule != null && classModule != currentModule && !ModuleRootManager.getInstance(currentModule).isDependsOn(classModule)) { - final OrderEntryFix fix = new OrderEntryFix() { - @Override - @NotNull - public String getText() { - return QuickFixBundle.message("orderEntry.fix.add.dependency.on.module", classModule.getName()); - } - - @Override - @NotNull - public String getFamilyName() { - return QuickFixBundle.message("orderEntry.fix.family.add.module.dependency"); - } - - @Override - public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - return !project.isDisposed() && !classModule.isDisposed() && !currentModule.isDisposed(); - } - - @Override - public void invoke(@NotNull final Project project, @Nullable final Editor editor, PsiFile file) { - final Runnable doit = new Runnable() { - @Override - public void run() { - final boolean test = ModuleRootManager.getInstance(currentModule).getFileIndex().isInTestSourceContent(classVFile); - ModuleRootModificationUtil.addDependency(currentModule, classModule, - test ? DependencyScope.TEST : DependencyScope.COMPILE, false); - if (editor != null) { - final List targetClasses = new ArrayList(); - for (PsiClass psiClass : classes) { - if (ModuleUtilCore.findModuleForPsiElement(psiClass) == classModule) { - targetClasses.add(psiClass); - } - } - new AddImportAction(project, reference, editor, targetClasses.toArray(new PsiClass[targetClasses.size()])).execute(); - } - } - }; - final Pair circularModules = ModuleCompilerUtil.addingDependencyFormsCircularity(currentModule, classModule); - if (circularModules == null) { - doit.run(); - } - else { - showCircularWarningAndContinue(project, circularModules, classModule, doit); - } - } - }; - registrar.register(fix); - result.add(fix); - } ModuleFileIndex moduleFileIndex = ModuleRootManager.getInstance(currentModule).getFileIndex(); for (OrderEntry orderEntry : fileIndex.getOrderEntriesForFile(virtualFile)) { if (orderEntry instanceof LibraryOrderEntry) { @@ -349,26 +296,6 @@ public abstract class OrderEntryFix implements IntentionAction, LocalQuickFix { Collections.emptyList(), inTests ? DependencyScope.TEST : DependencyScope.COMPILE); } - private static void showCircularWarningAndContinue(final Project project, final Pair circularModules, - final Module classModule, - final Runnable doit) { - final String message = QuickFixBundle.message("orderEntry.fix.circular.dependency.warning", classModule.getName(), - circularModules.getFirst().getName(), circularModules.getSecond().getName()); - if (ApplicationManager.getApplication().isUnitTestMode()) throw new RuntimeException(message); - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - if (!project.isOpen()) return; - int ret = Messages.showOkCancelDialog(project, message, - QuickFixBundle.message("orderEntry.fix.title.circular.dependency.warning"), - Messages.getWarningIcon()); - if (ret == 0) { - ApplicationManager.getApplication().runWriteAction(doit); - } - } - }); - } - public static boolean ensureAnnotationsJarInPath(final Module module) { if (isAnnotationsJarInPath(module)) return true; if (module == null) return false;