diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 428adfc49dba..ae21ba67a7b1 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -29,7 +29,6 @@ - @@ -70,12 +69,10 @@ + + - - - - diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GrClassEnhancer.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GrClassEnhancer.java deleted file mode 100644 index 0340a9b76fe0..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GrClassEnhancer.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.jetbrains.plugins.groovy.lang.resolve; - -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiType; -import com.intellij.psi.ResolveState; -import com.intellij.psi.scope.PsiScopeProcessor; -import com.intellij.util.containers.MultiMap; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; - -/** - * @author Sergey Evdokimov - */ -public abstract class GrClassEnhancer { - - private static final ExtensionPointName EP_NAME = ExtensionPointName.create("org.intellij.groovy.classEnhancer"); - - private static volatile MultiMap ourEnhancers; - - @NotNull - public abstract String getQualifiedName(); - - public abstract boolean processDynamicElements(@NotNull PsiType qualifierType, - @NotNull PsiClass aClass, - PsiScopeProcessor processor, - GroovyPsiElement place, - ResolveState state); - - public static MultiMap getEnhancers() { - MultiMap res = ourEnhancers; - if (ourEnhancers == null) { - res = new MultiMap(); - - for (GrClassEnhancer enhancer : EP_NAME.getExtensions()) { - res.putValue(enhancer.getQualifiedName(), enhancer); - } - - ourEnhancers = res; - } - return res; - } - -} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GrClassNonCodeMembersContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GrClassNonCodeMembersContributor.java deleted file mode 100644 index 46ccdf70f555..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GrClassNonCodeMembersContributor.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.jetbrains.plugins.groovy.lang.resolve; - -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiType; -import com.intellij.psi.ResolveState; -import com.intellij.psi.scope.PsiScopeProcessor; -import com.intellij.psi.util.InheritanceUtil; -import com.intellij.psi.util.PsiTypesUtil; -import com.intellij.util.containers.MultiMap; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; -import org.jetbrains.plugins.groovy.util.LightCacheKey; - -import java.util.HashSet; -import java.util.Set; - -/** - * @author Sergey Evdokimov - */ -public class GrClassNonCodeMembersContributor extends NonCodeMembersContributor { - - private static final LightCacheKey KEY = LightCacheKey.create(); - - @Override - public void processDynamicElements(@NotNull final PsiType qualifierType, - final PsiScopeProcessor processor, - final GroovyPsiElement place, - final ResolveState state) { - final PsiClass aClass = PsiTypesUtil.getPsiClass(qualifierType); - if (aClass == null) return; - - String[] superClassNames = KEY.getCachedValue(aClass); - if (superClassNames == null) { - Set superClasses = new HashSet(); - superClasses.add(aClass); - InheritanceUtil.getSuperClasses(aClass, superClasses, true); - - - superClassNames = new String[superClasses.size()]; - int i = 0; - for (PsiClass superClass : superClasses) { - superClassNames[i++] = superClass.getQualifiedName(); - } - - superClassNames = KEY.putCachedValue(aClass, superClassNames); - } - - final MultiMap enhancers = GrClassEnhancer.getEnhancers(); - - for (String superClassName : superClassNames) { - for (GrClassEnhancer enhancer : enhancers.get(superClassName)) { - if (!enhancer.processDynamicElements(qualifierType, aClass, processor, place, state)) return; - } - } - } -} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/NonCodeMembersContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/NonCodeMembersContributor.java index b2625833b872..aca2c69443c2 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/NonCodeMembersContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/NonCodeMembersContributor.java @@ -17,14 +17,22 @@ package org.jetbrains.plugins.groovy.lang.resolve; import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiType; import com.intellij.psi.ResolveState; import com.intellij.psi.scope.DelegatingScopeProcessor; import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTypesUtil; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.dsl.GroovyDslFileIndex; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import org.jetbrains.plugins.groovy.util.LightCacheKey; + +import java.util.*; /** * @author peter @@ -32,11 +40,45 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; public abstract class NonCodeMembersContributor { private static final ExtensionPointName EP_NAME = ExtensionPointName.create("org.intellij.groovy.membersContributor"); - public abstract void processDynamicElements(@NotNull PsiType qualifierType, + private static final LightCacheKey KEY = LightCacheKey.create(); + + private static volatile MultiMap ourClassSpecifiedContributors; + private static NonCodeMembersContributor[] ourAllTypeContributors; + + public void processDynamicElements(@NotNull PsiType qualifierType, PsiScopeProcessor processor, GroovyPsiElement place, - ResolveState state); + ResolveState state) { + throw new RuntimeException("One of two 'processDynamicElements()' methods must be implemented"); + } + public void processDynamicElements(@NotNull PsiType qualifierType, + PsiClass aClass, + PsiScopeProcessor processor, + GroovyPsiElement place, + ResolveState state) { + processDynamicElements(qualifierType, processor, place, state); + } + + @Nullable + protected String getParentClassName() { + return null; + } + + private static void ensureInit() { + if (ourClassSpecifiedContributors != null) return; + + MultiMap contributorMap = new MultiMap(); + + for (final NonCodeMembersContributor contributor : EP_NAME.getExtensions()) { + contributorMap.putValue(contributor.getParentClassName(), contributor); + } + + Collection allTypeContributors = contributorMap.remove(null); + ourAllTypeContributors = allTypeContributors.toArray(new NonCodeMembersContributor[allTypeContributors.size()]); + ourClassSpecifiedContributors = contributorMap; + } + public static boolean runContributors(@NotNull final PsiType qualifierType, PsiScopeProcessor processor, final GroovyPsiElement place, @@ -44,15 +86,50 @@ public abstract class NonCodeMembersContributor { MyDelegatingScopeProcessor delegatingProcessor = new MyDelegatingScopeProcessor(processor); - for (final NonCodeMembersContributor contributor : EP_NAME.getExtensions()) { - contributor.processDynamicElements(qualifierType, delegatingProcessor, place, state); + ensureInit(); + + final PsiClass aClass = PsiTypesUtil.getPsiClass(qualifierType); + + if (aClass != null) { + for (String superClassName : getParentClassNames(aClass)) { + for (NonCodeMembersContributor enhancer : ourClassSpecifiedContributors.get(superClassName)) { + enhancer.processDynamicElements(qualifierType, aClass, delegatingProcessor, place, state); + if (!delegatingProcessor.wantMore) { + return false; + } + } + } + } + + for (NonCodeMembersContributor contributor : ourAllTypeContributors) { + contributor.processDynamicElements(qualifierType, aClass, delegatingProcessor, place, state); if (!delegatingProcessor.wantMore) { return false; } } + return GroovyDslFileIndex.processExecutors(qualifierType, place, processor, state); } + protected static String[] getParentClassNames(PsiClass aClass) { + String[] superClassNames = KEY.getCachedValue(aClass); + if (superClassNames == null) { + Set superClasses = new HashSet(); + superClasses.add(aClass); + InheritanceUtil.getSuperClasses(aClass, superClasses, true); + + superClassNames = new String[superClasses.size()]; + int i = 0; + for (PsiClass superClass : superClasses) { + superClassNames[i++] = superClass.getQualifiedName(); + } + + superClassNames = KEY.putCachedValue(aClass, superClassNames); + } + + return superClassNames; + } + private static class MyDelegatingScopeProcessor extends DelegatingScopeProcessor { public boolean wantMore = true; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/GrCollectionTypeMembersProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/GrCollectionTypeMembersProvider.java index cd2d5fb154ab..d6615c3db993 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/GrCollectionTypeMembersProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/GrCollectionTypeMembersProvider.java @@ -24,32 +24,31 @@ import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; -import org.jetbrains.plugins.groovy.lang.resolve.GrClassEnhancer; +import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint; /** * @author Maxim.Medvedev */ -public class GrCollectionTypeMembersProvider extends GrClassEnhancer { +public class GrCollectionTypeMembersProvider extends NonCodeMembersContributor { - @NotNull @Override - public String getQualifiedName() { + public String getParentClassName() { return CommonClassNames.JAVA_UTIL_COLLECTION; } @Override - public boolean processDynamicElements(final @NotNull PsiType qualifierType, - @NotNull PsiClass aClass, + public void processDynamicElements(final @NotNull PsiType qualifierType, + PsiClass aClass, final PsiScopeProcessor processor, final GroovyPsiElement place, final ResolveState state) { final PsiType collectionType = PsiUtil.extractIterableTypeParameter(qualifierType, true); - if (collectionType == null) return true; + if (collectionType == null) return; final PsiScopeProcessor fieldSearcher = new FieldSearcher(processor, JavaPsiFacade.getInstance(place.getProject()).findClass(CommonClassNames.JAVA_UTIL_COLLECTION, place.getResolveScope())); - return ResolveUtil.processAllDeclarations(collectionType, fieldSearcher, state, place); + ResolveUtil.processAllDeclarations(collectionType, fieldSearcher, state, place); } private static class FieldSearcher implements PsiScopeProcessor, ClassHint, ElementClassHint { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/spock/SpockMemberContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/spock/SpockMemberContributor.java index bfb5c76d624b..3cad6a8569de 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/spock/SpockMemberContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/spock/SpockMemberContributor.java @@ -8,7 +8,7 @@ import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; -import org.jetbrains.plugins.groovy.lang.resolve.GrClassEnhancer; +import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint; @@ -17,43 +17,40 @@ import java.util.Map; /** * @author Sergey Evdokimov */ -public class SpockMemberContributor extends GrClassEnhancer { +public class SpockMemberContributor extends NonCodeMembersContributor { @Override - public boolean processDynamicElements(@NotNull PsiType qualifierType, - @NotNull PsiClass aClass, - PsiScopeProcessor processor, - GroovyPsiElement place, - ResolveState state) { + public void processDynamicElements(@NotNull PsiType qualifierType, + PsiClass aClass, + PsiScopeProcessor processor, + GroovyPsiElement place, + ResolveState state) { ClassHint classHint = processor.getHint(ClassHint.KEY); - if (classHint != null && !classHint.shouldProcess(ClassHint.ResolveKind.PROPERTY)) return true; + if (classHint != null && !classHint.shouldProcess(ClassHint.ResolveKind.PROPERTY)) return; GrMethod method = PsiTreeUtil.getParentOfType(place, GrMethod.class); - if (method == null) return true; + if (method == null) return; - if (aClass != method.getContainingClass()) return true; + if (aClass != method.getContainingClass()) return; Map cachedValue = SpockUtils.getVariableMap(method); String nameHint = ResolveUtil.getNameHint(processor); if (nameHint == null) { for (SpockVariableDescriptor spockVar : cachedValue.values()) { - if (!processor.execute(spockVar.getVariable(), state)) return false; + if (!processor.execute(spockVar.getVariable(), state)) return; } } else { SpockVariableDescriptor spockVar = cachedValue.get(nameHint); if (spockVar != null && spockVar.getNavigationElement() != place) { - if (!processor.execute(spockVar.getVariable(), state)) return false; + if (!processor.execute(spockVar.getVariable(), state)) return; } } - - return true; } - @NotNull @Override - public String getQualifiedName() { + public String getParentClassName() { return SpockUtils.SPEC_CLASS_NAME; } }