diff --git a/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java b/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java index 595e689bd956..0276d8308b15 100644 --- a/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java +++ b/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java @@ -24,10 +24,8 @@ import com.intellij.psi.util.MethodSignature; import com.intellij.psi.util.MethodSignatureUtil; import com.intellij.psi.util.PsiSuperMethodUtil; import com.intellij.psi.util.TypeConversionUtil; -import com.intellij.util.containers.FactoryMap; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.*; @@ -66,12 +64,11 @@ public class FindSuperElementsHelper { } public static PsiMethod getSiblingInheritedViaSubClass(@NotNull PsiMethod method) { - return Pair.getFirst(getSiblingInheritedViaSubClass(method, createSubClassCache())); + return Pair.getFirst(getSiblingInfoInheritedViaSubClass(method)); } // returns super method, sub class - public static Pair getSiblingInheritedViaSubClass(@NotNull final PsiMethod method, - @NotNull Map subClassCache) { + public static Pair getSiblingInfoInheritedViaSubClass(@NotNull final PsiMethod method) { if (!method.hasModifierProperty(PsiModifier.PUBLIC)) return null; if (method.hasModifierProperty(PsiModifier.STATIC)) return null; final PsiClass containingClass = method.getContainingClass(); @@ -86,55 +83,44 @@ public class FindSuperElementsHelper { final Ref> result = Ref.create(); ClassInheritorsSearch.search(containingClass, containingClass.getUseScope(), true, true, false).forEach( inheritor -> { + ProgressManager.checkCanceled(); + for (PsiClassType interfaceType : inheritor.getImplementsListTypes()) { ProgressManager.checkCanceled(); - for (PsiClassType interfaceType : inheritor.getImplementsListTypes()) { + PsiClassType.ClassResolveResult resolved = interfaceType.resolveGenerics(); + PsiClass anInterface = resolved.getElement(); + if (anInterface == null || !checkedInterfaces.add(PsiAnchor.create(anInterface))) continue; + for (PsiMethod superMethod : anInterface.findMethodsByName(method.getName(), true)) { + PsiElement navigationElement = superMethod.getNavigationElement(); + if (!(navigationElement instanceof PsiMethod)) continue; // Kotlin + superMethod = (PsiMethod)navigationElement; ProgressManager.checkCanceled(); - PsiClassType.ClassResolveResult resolved = interfaceType.resolveGenerics(); - PsiClass anInterface = resolved.getElement(); - if (anInterface == null || !checkedInterfaces.add(PsiAnchor.create(anInterface))) continue; - for (PsiMethod superMethod : anInterface.findMethodsByName(method.getName(), true)) { - PsiElement navigationElement = superMethod.getNavigationElement(); - if (!(navigationElement instanceof PsiMethod)) continue; // Kotlin - superMethod = (PsiMethod)navigationElement; - ProgressManager.checkCanceled(); - PsiClass superInterface = superMethod.getContainingClass(); - if (superInterface == null) { - continue; - } - if (containingClass.isInheritor(superInterface, true)) { - // if containingClass implements the superInterface then it's not a sibling inheritance but a pretty boring the usual one - continue; - } - - // calculate substitutor of containingClass --> inheritor - PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(containingClass, inheritor, PsiSubstitutor.EMPTY); - // calculate substitutor of inheritor --> superInterface - substitutor = TypeConversionUtil.getSuperClassSubstitutor(superInterface, inheritor, substitutor); - - final MethodSignature superSignature = superMethod.getSignature(substitutor); - final MethodSignature derivedSignature = method.getSignature(PsiSubstitutor.EMPTY); - boolean isOverridden = MethodSignatureUtil.isSubsignature(superSignature, derivedSignature); - - if (!isOverridden) { - continue; - } - result.set(Pair.create(superMethod, inheritor)); - return false; + PsiClass superInterface = superMethod.getContainingClass(); + if (superInterface == null) { + continue; } + if (containingClass.isInheritor(superInterface, true)) { + // if containingClass implements the superInterface then it's not a sibling inheritance but a pretty boring the usual one + continue; + } + + // calculate substitutor of containingClass --> inheritor + PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(containingClass, inheritor, PsiSubstitutor.EMPTY); + // calculate substitutor of inheritor --> superInterface + substitutor = TypeConversionUtil.getSuperClassSubstitutor(superInterface, inheritor, substitutor); + + final MethodSignature superSignature = superMethod.getSignature(substitutor); + final MethodSignature derivedSignature = method.getSignature(PsiSubstitutor.EMPTY); + boolean isOverridden = MethodSignatureUtil.isSubsignature(superSignature, derivedSignature); + + if (!isOverridden) { + continue; + } + result.set(Pair.create(superMethod, inheritor)); + return false; } - return true; + } + return true; }); return result.get(); } - - @NotNull - public static Map createSubClassCache() { - return new FactoryMap() { - @Nullable - @Override - protected PsiClass create(PsiClass aClass) { - return ClassInheritorsSearch.search(aClass, false).findFirst(); - } - }; - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java b/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java index 2fd6c3d60f50..7cd6659be3aa 100644 --- a/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -299,7 +299,7 @@ public class CodeInsightUtil { }); } else { Query baseQuery = ClassInheritorsSearch.search( - new ClassInheritorsSearch.SearchParameters(baseClass, scope, true, false, false, matcher::prefixMatches)); + new ClassInheritorsSearch.SearchParameters(baseClass, scope, true, true, false, matcher::prefixMatches)); Query query = new FilteredQuery<>(baseQuery, psiClass -> !(psiClass instanceof PsiTypeParameter)); query.forEach(inheritorsProcessor); } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java index 8e8635f436d9..0168d7def31d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java @@ -27,12 +27,12 @@ import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.openapi.editor.markup.SeparatorPlacement; import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.FindSuperElementsHelper; import com.intellij.psi.search.searches.AllOverridingMethodsSearch; +import com.intellij.psi.search.searches.DirectClassInheritorsSearch; import com.intellij.psi.search.searches.FunctionalExpressionSearch; import com.intellij.psi.search.searches.SuperMethodsSearch; import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; @@ -48,7 +48,6 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Set; public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor { @@ -162,7 +161,6 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor { @Override public void collectSlowLineMarkers(@NotNull final List elements, @NotNull final Collection result) { ApplicationManager.getApplication().assertReadAccessAllowed(); - Map subClassCache = FindSuperElementsHelper.createSubClassCache(); Collection methods = new THashSet<>(); //noinspection ForLoopReplaceableByForEach @@ -178,18 +176,17 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor { } } else if (parent instanceof PsiClass && !(parent instanceof PsiTypeParameter)) { - collectInheritingClasses((PsiClass)parent, result, subClassCache); + collectInheritingClasses((PsiClass)parent, result); } } if (!methods.isEmpty()) { collectOverridingMethods(methods, result); - collectSiblingInheritedMethods(methods, result, subClassCache); + collectSiblingInheritedMethods(methods, result); } } private static void collectSiblingInheritedMethods(@NotNull final Collection methods, - @NotNull Collection result, - @NotNull Map subClassCache) { + @NotNull Collection result) { for (PsiMethod method : methods) { ProgressManager.checkCanceled(); PsiClass aClass = method.getContainingClass(); @@ -198,7 +195,7 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor { boolean canHaveSiblingSuper = !method.hasModifierProperty(PsiModifier.ABSTRACT) && !method.hasModifierProperty(PsiModifier.STATIC) && method.hasModifierProperty(PsiModifier.PUBLIC)&& !method.hasModifierProperty(PsiModifier.FINAL)&& !method.hasModifierProperty(PsiModifier.NATIVE); if (!canHaveSiblingSuper) continue; - PsiMethod siblingInheritedViaSubClass = Pair.getFirst(FindSuperElementsHelper.getSiblingInheritedViaSubClass(method, subClassCache)); + PsiMethod siblingInheritedViaSubClass = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method); if (siblingInheritedViaSubClass == null) { continue; } @@ -229,14 +226,13 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor { } protected void collectInheritingClasses(@NotNull PsiClass aClass, - @NotNull Collection result, - @NotNull Map subClassCache) { + @NotNull Collection result) { if (aClass.hasModifierProperty(PsiModifier.FINAL)) { return; } if (CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName())) return; // It's useless to have overridden markers for object. - PsiClass subClass = subClassCache.get(aClass); + PsiClass subClass = DirectClassInheritorsSearch.search(aClass).findFirst(); if (subClass != null || FunctionalExpressionSearch.search(aClass).findFirst() != null) { final Icon icon; if (aClass.isInterface()) { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java index 0b2769b91e4c..7b4e4fd2777b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java @@ -160,8 +160,7 @@ public class MarkerType { } @Nullable private static String calculateOverridingSiblingMethodTooltip(@NotNull PsiMethod method) { - Pair pair = - FindSuperElementsHelper.getSiblingInheritedViaSubClass(method, FindSuperElementsHelper.createSubClassCache()); + Pair pair = FindSuperElementsHelper.getSiblingInfoInheritedViaSubClass(method); if (pair == null) return null; PsiMethod superMethod = pair.getFirst(); PsiClass subClass = pair.getSecond(); @@ -247,7 +246,7 @@ public class MarkerType { private static String getOverriddenMethodTooltip(@NotNull PsiMethod method) { PsiElementProcessor.CollectElementsWithLimit processor = new PsiElementProcessor.CollectElementsWithLimit(5); - OverridingMethodsSearch.search(method, true).forEach(new PsiElementProcessorAdapter(processor)); + OverridingMethodsSearch.search(method).forEach(new PsiElementProcessorAdapter(processor)); boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT); @@ -287,7 +286,7 @@ public class MarkerType { if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { @Override public void run() { - OverridingMethodsSearch.search(method, true).forEach(new PsiElementProcessorAdapter(collectProcessor)); + OverridingMethodsSearch.search(method).forEach(new PsiElementProcessorAdapter(collectProcessor)); if (isAbstract && collectProcessor.getCollection().size() < 2) { final PsiClass aClass = ApplicationManager.getApplication().runReadAction(new Computable() { @Override @@ -468,7 +467,7 @@ public class MarkerType { @Override public void run(@NotNull final ProgressIndicator indicator) { super.run(indicator); - OverridingMethodsSearch.search(myMethod, true).forEach( + OverridingMethodsSearch.search(myMethod).forEach( new CommonProcessors.CollectProcessor() { @Override public boolean process(PsiMethod psiMethod) { diff --git a/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java b/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java index 489e5a187569..5b5ee108e9a2 100644 --- a/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java +++ b/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java @@ -56,6 +56,7 @@ public class ClassInheritorsSearch extends ExtensibleQueryFactoryalwaysTrue() ? "" : " condition: "+myNameCondition); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SearchParameters that = (SearchParameters)o; + + if (myCheckDeep != that.myCheckDeep) return false; + if (myCheckInheritance != that.myCheckInheritance) return false; + if (myIncludeAnonymous != that.myIncludeAnonymous) return false; + if (!myClass.equals(that.myClass)) return false; + if (!myScope.equals(that.myScope)) return false; + return myNameCondition.equals(that.myNameCondition); + } + + @Override + public int hashCode() { + int result = myClass.hashCode(); + result = 31 * result + myScope.hashCode(); + result = 31 * result + (myCheckDeep ? 1 : 0); + result = 31 * result + (myCheckInheritance ? 1 : 0); + result = 31 * result + (myIncludeAnonymous ? 1 : 0); + result = 31 * result + myNameCondition.hashCode(); + return result; + } } private ClassInheritorsSearch() {} diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/HighlightingCaches.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/HighlightingCaches.java new file mode 100644 index 000000000000..66e2e49387df --- /dev/null +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/HighlightingCaches.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2016 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.psi.impl.search; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.impl.AnyPsiChangeListener; +import com.intellij.psi.impl.PsiManagerImpl; +import com.intellij.psi.search.searches.ClassInheritorsSearch; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicIntegerArray; + +class HighlightingCaches { + public static HighlightingCaches getInstance(Project project) { + return ServiceManager.getService(project, HighlightingCaches.class); + } + + private final List> allCaches = ContainerUtil.createConcurrentList(); + + public HighlightingCaches(Project project) { + project.getMessageBus().connect().subscribe(PsiManagerImpl.ANY_PSI_CHANGE_TOPIC, new AnyPsiChangeListener() { + @Override + public void beforePsiChanged(boolean isPhysical) { + if (isPhysical) { + allCaches.forEach(Map::clear); + } + } + + @Override + public void afterPsiChanged(boolean isPhysical) { + + } + }); + } + + final Map, AtomicIntegerArray>> DIRECT_SUB_CLASSES = createCache(); + final Map> ALL_SUB_CLASSES = createCache(); + final Map> OVERRIDING_METHODS = createCache(); + + @NotNull + private Map createCache() { + ConcurrentMap map = ContainerUtil.createConcurrentSoftKeySoftValueMap(10, 0.7f, Runtime.getRuntime().availableProcessors(), ContainerUtil.canonicalStrategy()); + allCaches.add(map); + return map; + } +} diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaClassInheritorsSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaClassInheritorsSearcher.java index 4d8de322a56d..7586c36fd995 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaClassInheritorsSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaClassInheritorsSearcher.java @@ -18,7 +18,6 @@ package com.intellij.psi.impl.search; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.QueryExecutorBase; import com.intellij.openapi.application.ReadActionProcessor; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.progress.ProgressManager; @@ -33,22 +32,22 @@ import com.intellij.psi.search.searches.AllClassesSearch; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.search.searches.DirectClassInheritorsSearch; import com.intellij.psi.util.PsiUtilCore; +import com.intellij.util.CommonProcessors; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Stack; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.Collection; import java.util.Set; public class JavaClassInheritorsSearcher extends QueryExecutorBase { - private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.search.JavaClassInheritorsSearcher"); - @Override public void processQuery(@NotNull ClassInheritorsSearch.SearchParameters parameters, @NotNull Processor consumer) { final PsiClass baseClass = parameters.getClassToProcess(); - final SearchScope searchScope = parameters.getScope(); - - LOG.assertTrue(searchScope != null); + assert parameters.isCheckDeep(); + assert parameters.isCheckInheritance(); ProgressIndicator progress = ProgressIndicatorProvider.getGlobalProgressIndicator(); if (progress != null) { @@ -67,7 +66,6 @@ public class JavaClassInheritorsSearcher extends QueryExecutorBase { - ProgressManager.checkCanceled(); - return isJavaLangObject(aClass) || consumer.process(aClass); + ProgressManager.checkCanceled(); + return isJavaLangObject(aClass) || consumer.process(aClass); }); } + Collection cached = HighlightingCaches.getInstance(project).ALL_SUB_CLASSES.get(parameters); + if (cached == null) { + cached = new ArrayList<>(); + boolean success = getAllSubClasses(project, baseClass, parameters, new CommonProcessors.CollectProcessor<>(cached)); + assert success; + HighlightingCaches.getInstance(project).ALL_SUB_CLASSES.put(parameters, cached); + } + + Processor readActionedConsumer = ReadActionProcessor.wrapInReadAction(consumer); + for (final PsiClass subClass : cached) { + ProgressManager.checkCanceled(); + if (!readActionedConsumer.process(subClass)) { + return false; + } + } + return true; + } + + private static boolean getAllSubClasses(@NotNull Project project, + @NotNull PsiClass baseClass, + @NotNull ClassInheritorsSearch.SearchParameters parameters, + @NotNull Processor consumer) { + SearchScope searchScope = parameters.getScope(); final Ref currentBase = Ref.create(null); final Stack stack = new Stack<>(); final Set processed = ContainerUtil.newTroveSet(); @@ -93,7 +114,7 @@ public class JavaClassInheritorsSearcher extends QueryExecutorBase { - stack.push(PsiAnchor.create(baseClass)); + stack.push(PsiAnchor.create(baseClass)); }); final GlobalSearchScope projectScope = GlobalSearchScope.allScope(project); - + while (!stack.isEmpty()) { ProgressManager.checkCanceled(); diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java index cf26fa137243..a5ea8242b66c 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java @@ -20,6 +20,7 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -27,6 +28,7 @@ import com.intellij.psi.impl.java.stubs.index.JavaAnonymousClassBaseRefOccurence import com.intellij.psi.impl.java.stubs.index.JavaSuperClassNameOccurenceIndex; import com.intellij.psi.search.EverythingGlobalScope; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.PsiSearchScopeUtil; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.AllClassesSearch; import com.intellij.psi.search.searches.DirectClassInheritorsSearch; @@ -34,15 +36,12 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.Processor; import com.intellij.util.QueryExecutor; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.SmartList; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.atomic.AtomicIntegerArray; /** * @author max @@ -67,94 +66,155 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor)aClass::getName); - if (StringUtil.isEmpty(searchKey)) { + Pair, AtomicIntegerArray> pair = calculateDirectSubClasses(project, aClass); + List result = pair.getFirst(); + AtomicIntegerArray isInheritorFlag = pair.getSecond(); + + if (result.isEmpty()) { return true; } - Collection candidates = - MethodUsagesSearcher.resolveInReadAction(project, () -> JavaSuperClassNameOccurenceIndex.getInstance().get(searchKey, project, scope)); - - Map> classes = new HashMap<>(); - - for (final PsiReferenceList referenceList : candidates) { + final VirtualFile jarFile = getJarFile(aClass); + // iterate by same-FQN groups. For each group process only same-jar subclasses, or all of them if they are all outside the jarFile. + int groupStart = 0; + boolean sameJarClassFound = false; + for (int i = 0; i < result.size(); i++) { ProgressManager.checkCanceled(); - final PsiClass candidate = (PsiClass)ApplicationManager.getApplication().runReadAction((Computable)referenceList::getParent); - if (!checkInheritance(parameters, aClass, candidate, project)) continue; - - String fqn = ApplicationManager.getApplication().runReadAction((Computable)candidate::getQualifiedName); - List list = classes.get(fqn); - if (list == null) { - list = new ArrayList<>(); - classes.put(fqn, list); - } - list.add(candidate); - } - - if (!classes.isEmpty()) { - final VirtualFile jarFile = getJarFile(aClass); - for (List sameNamedClasses : classes.values()) { - ProgressManager.checkCanceled(); - if (!processSameNamedClasses(sameNamedClasses, jarFile, consumer)) return false; - } - } - - if (parameters.includeAnonymous()) { - Collection anonymousCandidates = - MethodUsagesSearcher.resolveInReadAction(project, () -> JavaAnonymousClassBaseRefOccurenceIndex.getInstance().get(searchKey, project, scope)); - - for (PsiAnonymousClass candidate : anonymousCandidates) { - ProgressManager.checkCanceled(); - if (!checkInheritance(parameters, aClass, candidate, project)) continue; - - if (!consumer.process(candidate)) return false; - } - - boolean isEnum = ApplicationManager.getApplication().runReadAction((Computable)aClass::isEnum); - if (isEnum) { - // abstract enum can be subclassed in the body - PsiField[] fields = ApplicationManager.getApplication().runReadAction((Computable)aClass::getFields); - for (final PsiField field : fields) { + PsiClass subClass = result.get(i); + if (subClass instanceof PsiAnonymousClass) { + // we reached anonymous classes tail, process them all and exit + if (!parameters.includeAnonymous()) { + return true; + } + for (; i < result.size(); i++) { ProgressManager.checkCanceled(); - if (field instanceof PsiEnumConstant) { - PsiEnumConstantInitializer initializingClass = - ApplicationManager.getApplication().runReadAction((Computable)((PsiEnumConstant)field)::getInitializingClass); - if (initializingClass != null) { - if (!consumer.process(initializingClass)) return false; - } + subClass = result.get(i); + if (!checkInheritance(parameters.isCheckInheritance(), aClass, subClass, project, isInheritorFlag, i)) continue; + if (!isInScope(scope, subClass)) continue; + if (!consumer.process(subClass)) return false; + } + return true; + } + + if (subClass == PsiUtil.NULL_PSI_CLASS) { + // the end of the same-FQN group. Process only same-jar classes in the group or the whole group if there were none. + if (!sameJarClassFound) { + for (int g=groupStart; g !p.isCheckInheritance() || candidate.isInheritor(aClass, false)); + private static boolean isInScope(GlobalSearchScope scope, PsiClass subClass) { + return ApplicationManager.getApplication().runReadAction((Computable)() -> PsiSearchScopeUtil.isInScope(scope, subClass)); } - private static boolean processSameNamedClasses(@NotNull List sameNamedClasses, - @Nullable VirtualFile jarFile, - @NotNull Processor consumer) { - // if there is a class from the same jar, prefer it - boolean sameJarClassFound = false; + private static final int INHERITANCE_UNKNOWN = 0; + private static final int INHERITANCE_YES = 1; + private static final int INHERITANCE_NO = 2; - if (jarFile != null && sameNamedClasses.size() > 1) { - for (PsiClass sameNamedClass : sameNamedClasses) { + // Returns pair ( list of direct subclasses, array of corresponding isInheritor flags ) + // The array initially contains INHERITANCE_UNKNOWN values, then the isInheritor() method result is cached in the array as INHERITANCE_YES or INHERITANCE_NO. + // The list starts with non-anonymous classes, ends with anonymous sub classes + // Regular classes grouped by their FQN. (Because among the same-named subclasses we should return only the same-jar ones, or all of them if there were none) + // The groups are separated with NULL_PSI_CLASS + @NotNull + private static Pair, AtomicIntegerArray> calculateDirectSubClasses(@NotNull Project project, @NotNull PsiClass baseClass) { + Pair, AtomicIntegerArray> cached = HighlightingCaches.getInstance(project).DIRECT_SUB_CLASSES.get(baseClass); + if (cached != null) { + return cached; + } + + final String className = ApplicationManager.getApplication().runReadAction((Computable)baseClass::getName); + if (StringUtil.isEmpty(className)) { + return Pair.create(Collections.emptyList(), new AtomicIntegerArray(0)); + } + GlobalSearchScope allScope = GlobalSearchScope.allScope(project); + Collection candidates = + MethodUsagesSearcher.resolveInReadAction(project, () -> JavaSuperClassNameOccurenceIndex.getInstance().get(className, project, allScope)); + + Map> classes = new HashMap<>(); + int count = 0; + + for (final PsiReferenceList referenceList : candidates) { + ProgressManager.checkCanceled(); + final PsiClass candidate = (PsiClass)ApplicationManager.getApplication().runReadAction((Computable)referenceList::getParent); + + String fqn = ApplicationManager.getApplication().runReadAction((Computable)candidate::getQualifiedName); + List list = classes.get(fqn); + if (list == null) { + list = new SmartList<>(); + classes.put(fqn, list); + } + list.add(candidate); + count++; + } + + Collection anonymousCandidates = + MethodUsagesSearcher.resolveInReadAction(project, () -> JavaAnonymousClassBaseRefOccurenceIndex.getInstance().get(className, project, allScope)); + + List result = new ArrayList<>(count+classes.size()+anonymousCandidates.size()+1); + for (Map.Entry> entry : classes.entrySet()) { + result.addAll(entry.getValue()); + result.add(PsiUtil.NULL_PSI_CLASS); + } + + result.addAll(anonymousCandidates); + + boolean isEnum = ApplicationManager.getApplication().runReadAction((Computable)baseClass::isEnum); + if (isEnum) { + // abstract enum can be subclassed in the body + PsiField[] fields = ApplicationManager.getApplication().runReadAction((Computable)baseClass::getFields); + for (final PsiField field : fields) { ProgressManager.checkCanceled(); - boolean fromSameJar = Comparing.equal(getJarFile(sameNamedClass), jarFile); - if (fromSameJar) { - sameJarClassFound = true; - if (!consumer.process(sameNamedClass)) return false; + if (field instanceof PsiEnumConstant) { + PsiEnumConstantInitializer initializingClass = + ApplicationManager.getApplication().runReadAction((Computable)((PsiEnumConstant)field)::getInitializingClass); + if (initializingClass != null) { + result.add(initializingClass); + } } } } - return sameJarClassFound || ContainerUtil.process(sameNamedClasses, consumer); + Pair, AtomicIntegerArray> pair = Pair.create(result, new AtomicIntegerArray(result.size())); + HighlightingCaches.getInstance(project).DIRECT_SUB_CLASSES.put(baseClass, pair); + return pair; + } + + private static boolean checkInheritance(boolean checkInheritance, + @NotNull PsiClass aClass, + @NotNull PsiClass candidate, + @NotNull Project project, + @NotNull AtomicIntegerArray isInheritorFlags, + int i) { + if (!checkInheritance) return true; + int cachedFlag = isInheritorFlags.get(i); + if (cachedFlag == INHERITANCE_YES) return true; + if (cachedFlag == INHERITANCE_NO) return false; + assert cachedFlag == INHERITANCE_UNKNOWN; + boolean isReallyInherited = MethodUsagesSearcher.resolveInReadAction(project, () -> candidate.isInheritor(aClass, false)); + isInheritorFlags.set(i, isReallyInherited ? INHERITANCE_YES : INHERITANCE_NO); + return isReallyInherited; } private static VirtualFile getJarFile(@NotNull PsiClass aClass) { diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaOverridingMethodsSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaOverridingMethodsSearcher.java index dae9ba210232..874e40d351be 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaOverridingMethodsSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaOverridingMethodsSearcher.java @@ -15,9 +15,14 @@ */ package com.intellij.psi.impl.search; +import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.PsiSearchScopeUtil; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.search.searches.OverridingMethodsSearch; @@ -29,56 +34,79 @@ import com.intellij.util.QueryExecutor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.LinkedHashSet; + /** * @author max */ public class JavaOverridingMethodsSearcher implements QueryExecutor { @Override - public boolean execute(@NotNull final OverridingMethodsSearch.SearchParameters p, @NotNull final Processor consumer) { - final PsiMethod method = p.getMethod(); - final SearchScope scope = p.getScope(); + public boolean execute(@NotNull final OverridingMethodsSearch.SearchParameters parameters, @NotNull final Processor consumer) { + final PsiMethod method = parameters.getMethod(); - final PsiClass parentClass = ApplicationManager.getApplication().runReadAction(new Computable() { - @Nullable - @Override - public PsiClass compute() { - return method.getContainingClass(); + Project project = ApplicationManager.getApplication().runReadAction((Computable)method::getProject); + Collection cached = HighlightingCaches.getInstance(project).OVERRIDING_METHODS.get(method); + if (cached == null) { + cached = compute(method, project); + HighlightingCaches.getInstance(project).OVERRIDING_METHODS.put(method, cached); + } + + final SearchScope scope = parameters.getScope(); + + for (final PsiMethod subMethod : cached) { + ProgressManager.checkCanceled(); + if (!ApplicationManager.getApplication().runReadAction((Computable)() -> PsiSearchScopeUtil.isInScope(scope, subMethod))) { + continue; } - }); - assert parentClass != null; - Processor inheritorsProcessor = new Processor() { - @Override - public boolean process(final PsiClass inheritor) { - PsiMethod found = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - @Nullable - public PsiMethod compute() { - return findOverridingMethod(inheritor, parentClass, method); - } - }); - return found == null || consumer.process(found) && p.isCheckDeep(); + if (!consumer.process(subMethod) || !parameters.isCheckDeep()) { + return false; } + } + return true; + } + + @NotNull + private static Collection compute(@NotNull PsiMethod method, @NotNull Project project) { + Collection result = new LinkedHashSet<>(); + + Application application = ApplicationManager.getApplication(); + final PsiClass containingClass = application.runReadAction((Computable)method::getContainingClass); + assert containingClass != null; + Processor inheritorsProcessor = inheritor -> { + PsiMethod found = application.runReadAction((Computable)() -> findOverridingMethod(project, inheritor, method, containingClass)); + if (found != null) { + result.add(found); + } + return true; }; - return ClassInheritorsSearch.search(parentClass, scope, true).forEach(inheritorsProcessor); + // use wider scope to handle public method defined in package-private class which is subclassed by public class in the same package which is subclassed by public class from another package with redefined method + SearchScope allScope = GlobalSearchScope.allScope(project); + boolean success = ClassInheritorsSearch.search(containingClass, allScope, true).forEach(inheritorsProcessor); + assert success; + return result; } @Nullable - private static PsiMethod findOverridingMethod(PsiClass inheritor, @NotNull PsiClass parentClass, PsiMethod method) { + private static PsiMethod findOverridingMethod(@NotNull Project project, + @NotNull PsiClass inheritor, + @NotNull PsiMethod method, + @NotNull PsiClass methodContainingClass) { String name = method.getName(); if (inheritor.findMethodsByName(name, false).length > 0) { - PsiMethod found = MethodSignatureUtil.findMethodBySuperSignature(inheritor, getSuperSignature(inheritor, parentClass, method), false); - if (found != null && isAcceptable(found, method)) { + PsiMethod found = MethodSignatureUtil.findMethodBySuperSignature(inheritor, getSuperSignature(inheritor, methodContainingClass, method), false); + if (found != null && isAcceptable(project, found, inheritor, method, methodContainingClass)) { return found; } } - if (parentClass.isInterface() && !inheritor.isInterface()) { //check for sibling implementation + if (methodContainingClass.isInterface() && !inheritor.isInterface()) { //check for sibling implementation final PsiClass superClass = inheritor.getSuperClass(); - if (superClass != null && !superClass.isInheritor(parentClass, true) && superClass.findMethodsByName(name, true).length > 0) { - MethodSignature signature = getSuperSignature(inheritor, parentClass, method); + if (superClass != null && !superClass.isInheritor(methodContainingClass, true) && superClass.findMethodsByName(name, true).length > 0) { + MethodSignature signature = getSuperSignature(inheritor, methodContainingClass, method); PsiMethod derived = MethodSignatureUtil.findMethodInSuperClassBySignatureInDerived(inheritor, superClass, signature, true); - if (derived != null && isAcceptable(derived, method)) { + if (derived != null && isAcceptable(project, derived, inheritor, method, methodContainingClass)) { return derived; } } @@ -94,10 +122,13 @@ public class JavaOverridingMethodsSearcher implements QueryExecutor> findMethodsAndTheirSubstitutorsByName(@NonNls String name, boolean checkBases) { + throw createException(); + } + + @NotNull + @Override + public List> getAllMethodsAndTheirSubstitutors() { + throw createException(); + } + + @Nullable + @Override + public PsiClass findInnerClassByName(@NonNls String name, boolean checkBases) { + throw createException(); + } + + @Nullable + @Override + public PsiElement getLBrace() { + throw createException(); + } + + @Nullable + @Override + public PsiElement getRBrace() { + throw createException(); + } + + @Nullable + @Override + public PsiIdentifier getNameIdentifier() { + throw createException(); + } + + @Override + public PsiElement getScope() { + throw createException(); + } + + @Override + public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) { + throw createException(); + } + + @Override + public boolean isInheritorDeep(PsiClass baseClass, @Nullable PsiClass classToByPass) { + throw createException(); + } + + @Nullable + @Override + public PsiClass getContainingClass() { + throw createException(); + } + + @NotNull + @Override + public Collection getVisibleSignatures() { + throw createException(); + } + + @Override + public PsiElement setName(@NonNls @NotNull String name) { + throw createException(); + } + } + + public static final PsiClass NULL_PSI_CLASS = new NullPsiClass(); } diff --git a/platform/core-api/src/com/intellij/psi/util/PsiUtilCore.java b/platform/core-api/src/com/intellij/psi/util/PsiUtilCore.java index 175dee7d52ac..2554af1dbfca 100644 --- a/platform/core-api/src/com/intellij/psi/util/PsiUtilCore.java +++ b/platform/core-api/src/com/intellij/psi/util/PsiUtilCore.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -59,7 +59,7 @@ import java.util.List; public class PsiUtilCore { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.PsiUtilCore"); public static final PsiElement NULL_PSI_ELEMENT = new NullPsiElement(); - private static class NullPsiElement implements PsiElement { + protected static class NullPsiElement implements PsiElement { @Override @NotNull public Project getProject() { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyLineMarkerProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyLineMarkerProvider.java index 915459e52835..a712182eec9d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyLineMarkerProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyLineMarkerProvider.java @@ -31,7 +31,6 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; -import com.intellij.psi.impl.FindSuperElementsHelper; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.search.searches.AllOverridingMethodsSearch; import com.intellij.psi.search.searches.SuperMethodsSearch; @@ -58,7 +57,10 @@ import org.jetbrains.plugins.groovy.lang.psi.util.GrTraitUtil; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils; import javax.swing.*; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; /** * @author ilyas @@ -85,7 +87,7 @@ public class GroovyLineMarkerProvider extends JavaLineMarkerProvider { final MarkerType type = GroovyMarkerTypes.OVERRIDING_PROPERTY_TYPE; return new LineMarkerInfo<>(element, element.getTextRange(), icon, Pass.UPDATE_ALL, type.getTooltip(), type.getNavigationHandler(), - GutterIconRenderer.Alignment.LEFT); + GutterIconRenderer.Alignment.LEFT); } } } @@ -95,7 +97,7 @@ public class GroovyLineMarkerProvider extends JavaLineMarkerProvider { final Icon icon = AllIcons.Gutter.OverridingMethod; final MarkerType type = GroovyMarkerTypes.GR_OVERRIDING_METHOD; return new LineMarkerInfo<>(element, element.getTextRange(), icon, Pass.UPDATE_ALL, type.getTooltip(), - type.getNavigationHandler(), GutterIconRenderer.Alignment.LEFT); + type.getNavigationHandler(), GutterIconRenderer.Alignment.LEFT); } } //need to draw method separator above docComment @@ -132,8 +134,8 @@ public class GroovyLineMarkerProvider extends JavaLineMarkerProvider { } LineMarkerInfo info = new LineMarkerInfo<>(element, comment != null ? comment.getTextRange() : element.getTextRange(), null, - Pass.UPDATE_ALL, FunctionUtil.nullConstant(), null, - GutterIconRenderer.Alignment.RIGHT); + Pass.UPDATE_ALL, FunctionUtil.nullConstant(), null, + GutterIconRenderer.Alignment.RIGHT); EditorColorsScheme scheme = myColorsManager.getGlobalScheme(); info.separatorColor = scheme.getColor(CodeInsightColors.METHOD_SEPARATORS_COLOR); info.separatorPlacement = SeparatorPlacement.TOP; @@ -186,7 +188,6 @@ public class GroovyLineMarkerProvider extends JavaLineMarkerProvider { @Override public void collectSlowLineMarkers(@NotNull final List elements, @NotNull final Collection result) { Set methods = new HashSet<>(); - Map subClassCache = FindSuperElementsHelper.createSubClassCache(); for (PsiElement element : elements) { ProgressManager.checkCanceled(); if (element instanceof GrField) { @@ -202,7 +203,7 @@ public class GroovyLineMarkerProvider extends JavaLineMarkerProvider { } } else if (element instanceof PsiClass && !(element instanceof PsiTypeParameter)) { - collectInheritingClasses((PsiClass)element, result, subClassCache); + collectInheritingClasses((PsiClass)element, result); } } collectOverridingMethods(methods, result); @@ -244,7 +245,7 @@ public class GroovyLineMarkerProvider extends JavaLineMarkerProvider { final MarkerType type = element instanceof GrField ? GroovyMarkerTypes.OVERRIDEN_PROPERTY_TYPE : GroovyMarkerTypes.GR_OVERRIDEN_METHOD; LineMarkerInfo info = new LineMarkerInfo<>(range, range.getTextRange(), icon, Pass.UPDATE_OVERRIDDEN_MARKERS, type.getTooltip(), - type.getNavigationHandler(), GutterIconRenderer.Alignment.RIGHT); + type.getNavigationHandler(), GutterIconRenderer.Alignment.RIGHT); result.add(info); } } diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index b21f85a85825..96de7d13c66a 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -446,6 +446,8 @@ serviceImplementation="com.intellij.psi.impl.PsiNameHelperImpl"/> +