diff --git a/java/java-psi-api/java-psi-api.iml b/java/java-psi-api/java-psi-api.iml index caff682b4a06..56cc8cf0ef77 100644 --- a/java/java-psi-api/java-psi-api.iml +++ b/java/java-psi-api/java-psi-api.iml @@ -8,6 +8,6 @@ + - - + \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiClassUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiClassUtil.java index 1f2d47996384..bdc71934dc59 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiClassUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiClassUtil.java @@ -16,9 +16,24 @@ package com.intellij.psi.util; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.OrderEntry; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiAnonymousClass; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiModifier; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; /** * @author mike @@ -42,4 +57,31 @@ public class PsiClassUtil { if (mustNotBeAbstract && aClass.hasModifierProperty(PsiModifier.ABSTRACT)) return false; return aClass.getContainingClass() == null || aClass.hasModifierProperty(PsiModifier.STATIC); } + + /** + * Searches the project for modules that contain the class with the specified full-qualified name within + * the module dependencies or libraries. + * + * @param qualifiedName the full-qualified name of the class to find. + * @return the modules that contain the given class in dependencies or libraries. + */ + @NotNull + public static Collection findModulesWithClass(@NotNull Project project, @NonNls @NotNull String qualifiedName) { + GlobalSearchScope allScope = GlobalSearchScope.allScope(project); + JavaPsiFacade facade = JavaPsiFacade.getInstance(project); + PsiClass[] possibleClasses = facade.findClasses(qualifiedName, allScope); + if (possibleClasses.length == 0) { + return Collections.emptyList(); + } + Set relevantModules = ContainerUtil.newLinkedHashSet(); + ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); + for (PsiClass aClass : possibleClasses) { + VirtualFile classFile = aClass.getContainingFile().getVirtualFile(); + for (OrderEntry orderEntry : fileIndex.getOrderEntriesForFile(classFile)) { + relevantModules.add(orderEntry.getOwnerModule()); + } + } + return relevantModules; + } + } diff --git a/java/java-tests/testSrc/com/intellij/psi/impl/cache/impl/FindClassTest.java b/java/java-tests/testSrc/com/intellij/psi/impl/cache/impl/FindClassTest.java index 988017df992f..c3825da78195 100644 --- a/java/java-tests/testSrc/com/intellij/psi/impl/cache/impl/FindClassTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/impl/cache/impl/FindClassTest.java @@ -16,9 +16,14 @@ package com.intellij.psi.impl.cache.impl; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ex.PathManagerEx; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileTypes.FileTypeManager; +import com.intellij.openapi.module.ModifiableModuleModel; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.module.StdModuleTypes; import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; @@ -29,11 +34,15 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiClassUtil; import com.intellij.testFramework.PsiTestCase; import com.intellij.testFramework.PsiTestUtil; +import com.intellij.util.containers.ContainerUtil; import java.io.File; import java.io.IOException; +import java.util.Collection; +import java.util.List; /** * @author max @@ -139,4 +148,64 @@ public class FindClassTest extends PsiTestCase { } }); } + + public void testMultipleModules() throws Exception { + List otherModules = configureTwoMoreModules(); + assertSize(2, otherModules); + PsiClass psiClass = myJavaFacade.findClass("p.A", getModule().getModuleWithDependenciesAndLibrariesScope(true)); + assertNotNull(psiClass); + assertEquals("p.A", psiClass.getQualifiedName()); + + PsiClass packClass1 = myJavaFacade.findClass("pack.MyClass", getModule().getModuleWithDependenciesAndLibrariesScope(true)); + assertNull(packClass1); + + PsiClass psiClass2 = myJavaFacade.findClass("p.A", otherModules.get(0).getModuleWithDependenciesAndLibrariesScope(true)); + assertNotNull(psiClass2); + assertEquals("p.A", psiClass2.getQualifiedName()); + + PsiClass packClass2 = myJavaFacade.findClass("pack.MyClass", otherModules.get(0).getModuleWithDependenciesAndLibrariesScope(true)); + assertNotNull(packClass2); + assertEquals("pack.MyClass", packClass2.getQualifiedName()); + + PsiClass psiClass3 = myJavaFacade.findClass("p.A", otherModules.get(1).getModuleWithDependenciesAndLibrariesScope(true)); + assertNull(psiClass3); + + PsiClass packClass3 = myJavaFacade.findClass("pack.MyClass", otherModules.get(1).getModuleWithDependenciesAndLibrariesScope(true)); + assertNull(packClass3); + } + + public void testFindModulesWithClass() throws Exception { + List otherModules = configureTwoMoreModules(); + assertSize(2, otherModules); + + PsiClass psiClass = myJavaFacade.findClass("p.A", getModule().getModuleWithDependenciesAndLibrariesScope(true)); + assertNotNull(psiClass); + PsiClass psiClass2 = myJavaFacade.findClass("p.A", otherModules.get(0).getModuleWithDependenciesAndLibrariesScope(true)); + assertNotNull(psiClass2); + Collection modules = PsiClassUtil.findModulesWithClass(myProject, "p.A"); + assertSameElements(modules, getModule(), otherModules.get(0)); + + PsiClass packClass = myJavaFacade.findClass("pack.MyClass", otherModules.get(0).getModuleWithDependenciesAndLibrariesScope(true)); + assertNotNull(packClass); + Collection packModules = PsiClassUtil.findModulesWithClass(myProject, "pack.MyClass"); + assertSameElements(packModules, otherModules.get(0)); + } + + private List configureTwoMoreModules() { + final List newModules = ContainerUtil.newArrayList(); + ApplicationManager.getApplication().runWriteAction(() -> { + ModifiableModuleModel modifiableModel = ModuleManager.getInstance(getProject()).getModifiableModel(); + Module module2 = modifiableModel.newModule("a.iml", StdModuleTypes.JAVA.getId()); + newModules.add(module2); + ModuleRootModificationUtil.addDependency(module2, getModule()); + File repoLib = new File(PathManagerEx.getTestDataPath(), "/psi/cls/repo/"); + VirtualFile repoRoot = LocalFileSystem.getInstance().findFileByIoFile(repoLib); + assertNotNull(repoRoot); + ModuleRootModificationUtil.addModuleLibrary(module2, repoRoot.getUrl()); + newModules.add(modifiableModel.newModule("b.iml", StdModuleTypes.JAVA.getId())); + modifiableModel.commit(); + }); + return newModules; + } + } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/console/GroovyConsoleUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/console/GroovyConsoleUtil.java index a9f11d162698..6b3b42cd0fb0 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/console/GroovyConsoleUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/console/GroovyConsoleUtil.java @@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.console; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.psi.JavaPsiFacade; @@ -32,6 +33,8 @@ import org.jetbrains.plugins.groovy.config.GroovyFacetUtil; import org.jetbrains.plugins.groovy.util.LibrariesUtil; import org.jetbrains.plugins.groovy.util.ModuleChooserUtil; +import java.util.Arrays; + public class GroovyConsoleUtil { public static final Condition APPLICABLE_MODULE = new Condition() { @@ -64,13 +67,17 @@ public class GroovyConsoleUtil { facade.findClass("groovy.ui.GroovyMain", scope) != null; } - public static void selectModuleAndRun(Project project, Consumer consumer) { - ModuleChooserUtil.selectModule(project, APPLICABLE_MODULE, MODULE_VERSION, consumer); + ModuleChooserUtil.selectModule(project, + ModuleChooserUtil.filterGroovyCompatibleModules( + Arrays.asList(ModuleManager.getInstance(project).getModules()), APPLICABLE_MODULE), + MODULE_VERSION, consumer); } public static void selectModuleAndRun(Project project, Consumer consumer, DataContext context) { - ModuleChooserUtil.selectModule(project, APPLICABLE_MODULE, MODULE_VERSION, consumer, context); + ModuleChooserUtil.selectModule(project, ModuleChooserUtil.filterGroovyCompatibleModules( + Arrays.asList(ModuleManager.getInstance(project).getModules()), APPLICABLE_MODULE), + MODULE_VERSION, consumer, context); } @Contract("null -> null; !null -> !null") diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/console/actions/GrNewConsoleAction.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/console/actions/GrNewConsoleAction.java index 677d95a359fc..65257a4afa8d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/console/actions/GrNewConsoleAction.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/console/actions/GrNewConsoleAction.java @@ -22,6 +22,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -30,6 +31,7 @@ import org.jetbrains.plugins.groovy.console.GroovyConsole; import org.jetbrains.plugins.groovy.console.GroovyConsoleRootType; import org.jetbrains.plugins.groovy.util.ModuleChooserUtil; +import java.util.Arrays; import java.util.List; import static org.jetbrains.plugins.groovy.console.GroovyConsoleUtil.APPLICABLE_MODULE; @@ -68,7 +70,8 @@ public class GrNewConsoleAction extends AnAction { if (moduleForFile != null) return moduleForFile; } - final List modules = ModuleChooserUtil.getGroovyCompatibleModules(project, APPLICABLE_MODULE); + final List modules = ModuleChooserUtil.filterGroovyCompatibleModules( + Arrays.asList(ModuleManager.getInstance(project).getModules()), APPLICABLE_MODULE); return modules.isEmpty() ? null : modules.get(0); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/shell/DefaultGroovyShellRunner.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/shell/DefaultGroovyShellRunner.java index 9654e6dc3b6a..7657f3b4a675 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/shell/DefaultGroovyShellRunner.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/shell/DefaultGroovyShellRunner.java @@ -18,10 +18,13 @@ package org.jetbrains.plugins.groovy.shell; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.JavaParameters; import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiClassUtil; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.config.AbstractConfigUtils; import org.jetbrains.plugins.groovy.config.GroovyConfigUtils; @@ -29,6 +32,9 @@ import org.jetbrains.plugins.groovy.runner.DefaultGroovyScriptRunner; import org.jetbrains.plugins.groovy.runner.GroovyScriptRunConfiguration; import org.jetbrains.plugins.groovy.util.LibrariesUtil; +import java.util.Collection; +import java.util.Set; + /** * @author Sergey Evdokimov */ @@ -66,12 +72,20 @@ public class DefaultGroovyShellRunner extends GroovyShellConfig { return version == AbstractConfigUtils.UNDEFINED_VERSION ? "" : "Groovy " + version; } + private final static String[] REQUIRED_GROOVY_CLASSES = { + "org.apache.commons.cli.CommandLineParser", + "org.codehaus.groovy.tools.shell.Main", + "org.fusesource.jansi.AnsiConsole"}; + public static boolean hasGroovyWithNeededJars(Module module) { GlobalSearchScope scope = GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module); JavaPsiFacade facade = JavaPsiFacade.getInstance(module.getProject()); - return facade.findClass("org.apache.commons.cli.CommandLineParser", scope) != null && - facade.findClass("org.codehaus.groovy.tools.shell.Main", scope) != null && - facade.findClass("org.fusesource.jansi.AnsiConsole", scope) != null; + for (String className : REQUIRED_GROOVY_CLASSES) { + if (facade.findClass(className, scope) == null) { + return false; + } + } + return true; } @Override @@ -79,6 +93,23 @@ public class DefaultGroovyShellRunner extends GroovyShellConfig { return super.isSuitableModule(module) && hasGroovyWithNeededJars(module); } + @Override + public Collection getPossiblySuitableModules(Project project) { + Set results = null; + for (String className : REQUIRED_GROOVY_CLASSES) { + Collection someModules = PsiClassUtil.findModulesWithClass(project, className); + if (results == null) { + results = ContainerUtil.newLinkedHashSet(someModules); + } else { + results.retainAll(someModules); + } + if (results.isEmpty()) { + return ContainerUtil.emptyList(); + } + } + return results; + } + @Override public String getTitle() { return "Groovy Shell"; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/shell/GroovyShellActionBase.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/shell/GroovyShellActionBase.java index 6567050466d6..663b35834f67 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/shell/GroovyShellActionBase.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/shell/GroovyShellActionBase.java @@ -31,6 +31,8 @@ import com.intellij.util.Function; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.util.ModuleChooserUtil; +import java.util.Collection; + public abstract class GroovyShellActionBase extends AnAction { private final GroovyShellConfig myConfig; @@ -77,7 +79,8 @@ public abstract class GroovyShellActionBase extends AnAction { @Nullable @Override public Result compute() { - return Result.create(ModuleChooserUtil.hasGroovyCompatibleModules(project, APPLICABLE_MODULE), + Collection possibleModules = myConfig.getPossiblySuitableModules(project); + return Result.create(ModuleChooserUtil.hasGroovyCompatibleModules(possibleModules, APPLICABLE_MODULE), ProjectRootModificationTracker.getInstance(project)); } }, false); @@ -87,6 +90,9 @@ public abstract class GroovyShellActionBase extends AnAction { public void actionPerformed(AnActionEvent e) { final Project project = e.getData(CommonDataKeys.PROJECT); assert project != null; - ModuleChooserUtil.selectModule(project, APPLICABLE_MODULE, VERSION_PROVIDER, RUNNER); + Collection suitableModules = ModuleChooserUtil.filterGroovyCompatibleModules(myConfig.getPossiblySuitableModules(project), + APPLICABLE_MODULE); + ModuleChooserUtil.selectModule(project, suitableModules, VERSION_PROVIDER, RUNNER); } + } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/shell/GroovyShellConfig.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/shell/GroovyShellConfig.java index a4946c23cb77..a32a53d8185c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/shell/GroovyShellConfig.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/shell/GroovyShellConfig.java @@ -18,11 +18,14 @@ package org.jetbrains.plugins.groovy.shell; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.JavaParameters; import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.config.GroovyFacetUtil; +import java.util.Collection; + public abstract class GroovyShellConfig { @NotNull @@ -46,5 +49,7 @@ public abstract class GroovyShellConfig { return GroovyFacetUtil.isSuitableModule(module); } + public abstract Collection getPossiblySuitableModules(Project project); + public abstract String getTitle(); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/util/ModuleChooserUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/util/ModuleChooserUtil.java index ef537637136a..a9ed8458fb08 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/util/ModuleChooserUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/util/ModuleChooserUtil.java @@ -18,7 +18,6 @@ package org.jetbrains.plugins.groovy.util; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.JavaSdkType; import com.intellij.openapi.projectRoots.Sdk; @@ -43,21 +42,21 @@ public class ModuleChooserUtil { private static final String GROOVY_LAST_MODULE = "Groovy.Last.Module.Chosen"; public static void selectModule(@NotNull Project project, - final Condition check, + final Collection suitableModules, final Function versionProvider, final Consumer callback) { - selectModule(project, check, versionProvider, callback, null); + selectModule(project, suitableModules, versionProvider, callback, null); } public static void selectModule(@NotNull Project project, - final Condition check, + final Collection suitableModules, final Function versionProvider, final Consumer callback, @Nullable DataContext context) { final List modules = new ArrayList(); final Map versions = new HashMap(); - for (Module module : getGroovyCompatibleModules(project, check)) { + for (Module module : suitableModules) { modules.add(module); versions.put(module, versionProvider.fun(module)); } @@ -132,11 +131,11 @@ public class ModuleChooserUtil { }; } - public static List getGroovyCompatibleModules(Project project, final Condition condition) { - return ContainerUtil.filter(ModuleManager.getInstance(project).getModules(), isGroovyCompatibleModule(condition)); + public static List filterGroovyCompatibleModules(Collection modules, final Condition condition) { + return ContainerUtil.filter(modules, isGroovyCompatibleModule(condition)); } - public static boolean hasGroovyCompatibleModules(Project project, final Condition condition) { - return ContainerUtil.or(ModuleManager.getInstance(project).getModules(), isGroovyCompatibleModule(condition)); + public static boolean hasGroovyCompatibleModules(Collection modules, final Condition condition) { + return ContainerUtil.or(modules, isGroovyCompatibleModule(condition)); } }