mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
implement SuperClassHasFrequentlyUsedInheritors inspection over compiler indices
This commit is contained in:
+25
-8
@@ -62,7 +62,7 @@ class CompilerReferenceReader {
|
||||
}
|
||||
else {
|
||||
LightRef.LightClassHierarchyElementDef hierarchyElement = ((LightRef.LightMember)ref).getOwner();
|
||||
hierarchy = getWholeHierarchy(hierarchyElement, checkBaseClassAmbiguity, -1);
|
||||
hierarchy = getHierarchy(hierarchyElement, checkBaseClassAmbiguity, false, -1);
|
||||
}
|
||||
if (hierarchy == null) return null;
|
||||
TIntHashSet set = new TIntHashSet();
|
||||
@@ -222,25 +222,28 @@ class CompilerReferenceReader {
|
||||
}
|
||||
|
||||
@Nullable("return null if the class hierarchy contains ambiguous qualified names")
|
||||
LightRef.NamedLightRef[] getWholeHierarchy(LightRef.LightClassHierarchyElementDef hierarchyElement, boolean checkBaseClassAmbiguity, int interruptNumber) {
|
||||
LightRef.LightClassHierarchyElementDef[] getHierarchy(LightRef.LightClassHierarchyElementDef hierarchyElement,
|
||||
boolean checkBaseClassAmbiguity,
|
||||
boolean includeAnonymous,
|
||||
int interruptNumber) {
|
||||
try {
|
||||
Set<LightRef.NamedLightRef> result = new THashSet<>();
|
||||
Queue<LightRef.NamedLightRef> q = new Queue<>(10);
|
||||
Set<LightRef.LightClassHierarchyElementDef> result = new THashSet<>();
|
||||
Queue<LightRef.LightClassHierarchyElementDef> q = new Queue<>(10);
|
||||
q.addLast(hierarchyElement);
|
||||
while (!q.isEmpty()) {
|
||||
LightRef.NamedLightRef curClass = q.pullFirst();
|
||||
LightRef.LightClassHierarchyElementDef curClass = q.pullFirst();
|
||||
if (interruptNumber != -1 && result.size() > interruptNumber) {
|
||||
break;
|
||||
}
|
||||
if (result.add(curClass)) {
|
||||
if (checkBaseClassAmbiguity || curClass != hierarchyElement) {
|
||||
if (!(curClass instanceof LightRef.LightAnonymousClassDef) && (checkBaseClassAmbiguity || curClass != hierarchyElement)) {
|
||||
if (hasMultipleDefinitions(curClass)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
myIndex.get(CompilerIndices.BACK_HIERARCHY).getData(curClass).forEach((id, children) -> {
|
||||
for (LightRef child : children) {
|
||||
if (child instanceof LightRef.LightClassHierarchyElementDef && !(child instanceof LightRef.LightAnonymousClassDef)) {
|
||||
if (child instanceof LightRef.LightClassHierarchyElementDef && (includeAnonymous || !(child instanceof LightRef.LightAnonymousClassDef))) {
|
||||
q.addLast((LightRef.LightClassHierarchyElementDef)child);
|
||||
}
|
||||
}
|
||||
@@ -248,13 +251,27 @@ class CompilerReferenceReader {
|
||||
});
|
||||
}
|
||||
}
|
||||
return result.toArray(LightRef.NamedLightRef.EMPTY_ARRAY);
|
||||
return result.toArray(LightRef.LightClassHierarchyElementDef.EMPTY_ARRAY);
|
||||
}
|
||||
catch (StorageException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
LightRef.LightClassHierarchyElementDef[] getDirectInheritors(LightRef.LightClassHierarchyElementDef hierarchyElement) throws StorageException {
|
||||
Set<LightRef.LightClassHierarchyElementDef> result = new THashSet<>();
|
||||
myIndex.get(CompilerIndices.BACK_HIERARCHY).getData(hierarchyElement).forEach((id, children) -> {
|
||||
for (LightRef child : children) {
|
||||
if (child instanceof LightRef.LightClassHierarchyElementDef && !(child instanceof LightRef.LightAnonymousClassDef)) {
|
||||
result.add((LightRef.LightClassHierarchyElementDef)child);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result.toArray(LightRef.LightClassHierarchyElementDef.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
private enum DefCount { NONE, ONE, MANY}
|
||||
private boolean hasMultipleDefinitions(LightRef.NamedLightRef def) throws StorageException {
|
||||
DefCount[] result = new DefCount[]{DefCount.NONE};
|
||||
|
||||
+8
-1
@@ -25,7 +25,7 @@ import org.jetbrains.jps.backwardRefs.SignatureData;
|
||||
import java.util.SortedSet;
|
||||
|
||||
/**
|
||||
* The service is used for java relevant chain completion
|
||||
* The service is used for java relevant chain completion / frequently used superclass inspection
|
||||
*/
|
||||
public abstract class CompilerReferenceServiceEx extends CompilerReferenceService {
|
||||
protected CompilerReferenceServiceEx(Project project) {
|
||||
@@ -43,4 +43,11 @@ public abstract class CompilerReferenceServiceEx extends CompilerReferenceServic
|
||||
@NotNull
|
||||
public abstract String getName(int idx)
|
||||
throws ReferenceIndexUnavailableException;
|
||||
|
||||
public abstract int getNameId(@NotNull String name) throws ReferenceIndexUnavailableException;
|
||||
|
||||
@NotNull
|
||||
public abstract LightRef.LightClassHierarchyElementDef[] getDirectInheritors(LightRef.LightClassHierarchyElementDef baseClass) throws ReferenceIndexUnavailableException;
|
||||
|
||||
public abstract int getInheritorCount(LightRef.LightClassHierarchyElementDef baseClass) throws ReferenceIndexUnavailableException;
|
||||
}
|
||||
|
||||
+55
-2
@@ -53,6 +53,7 @@ import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ConcurrentFactoryMap;
|
||||
import com.intellij.util.indexing.FileBasedIndex;
|
||||
import com.intellij.util.indexing.StorageException;
|
||||
@@ -235,7 +236,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
|
||||
.filter(r -> r instanceof LightRef.JavaLightMethodRef)
|
||||
.map(r -> (LightRef.JavaLightMethodRef) r)
|
||||
.flatMap(r -> {
|
||||
LightRef.NamedLightRef[] hierarchy = myReader.getWholeHierarchy(r.getOwner(), false, ChainSearchMagicConstants.MAX_HIERARCHY_SIZE);
|
||||
LightRef.NamedLightRef[] hierarchy = myReader.getHierarchy(r.getOwner(), false, false, ChainSearchMagicConstants.MAX_HIERARCHY_SIZE);
|
||||
return hierarchy == null ? Stream.empty() : Arrays.stream(hierarchy).map(c -> r.override(c.getName()));
|
||||
})
|
||||
.distinct()
|
||||
@@ -278,7 +279,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
|
||||
return false;
|
||||
}
|
||||
catch (Exception e) {
|
||||
onException(e, "correlation");
|
||||
onException(e, "conditional probability");
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
@@ -293,6 +294,58 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceServiceEx imp
|
||||
try {
|
||||
if (myReader == null) throw new ReferenceIndexUnavailableException();
|
||||
return myReader.getNameEnumerator().getName(idx);
|
||||
} catch (Exception e) {
|
||||
onException(e, "find methods");
|
||||
throw new ReferenceIndexUnavailableException();
|
||||
} finally {
|
||||
myReadDataLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNameId(@NotNull String name) throws ReferenceIndexUnavailableException {
|
||||
myReadDataLock.lock();
|
||||
try {
|
||||
if (myReader == null) throw new ReferenceIndexUnavailableException();
|
||||
int id;
|
||||
try {
|
||||
id = myReader.getNameEnumerator().tryEnumerate(name);
|
||||
}
|
||||
catch (Exception e) {
|
||||
onException(e, "get name-id");
|
||||
throw new ReferenceIndexUnavailableException();
|
||||
}
|
||||
return id;
|
||||
} finally {
|
||||
myReadDataLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LightRef.LightClassHierarchyElementDef[] getDirectInheritors(@NotNull LightRef.LightClassHierarchyElementDef baseClass) throws ReferenceIndexUnavailableException {
|
||||
myReadDataLock.lock();
|
||||
try {
|
||||
if (myReader == null) throw new ReferenceIndexUnavailableException();
|
||||
return myReader.getDirectInheritors(baseClass);
|
||||
} catch (Exception e) {
|
||||
onException(e, "find methods");
|
||||
throw new ReferenceIndexUnavailableException();
|
||||
} finally {
|
||||
myReadDataLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInheritorCount(@NotNull LightRef.LightClassHierarchyElementDef baseClass) throws ReferenceIndexUnavailableException {
|
||||
myReadDataLock.lock();
|
||||
try {
|
||||
if (myReader == null) throw new ReferenceIndexUnavailableException();
|
||||
LightRef.NamedLightRef[] hierarchy = ObjectUtils.notNull(myReader.getHierarchy(baseClass, false, true, -1));
|
||||
return hierarchy.length;
|
||||
} catch (Exception e) {
|
||||
onException(e, "inheritor count");
|
||||
throw new ReferenceIndexUnavailableException();
|
||||
} finally {
|
||||
myReadDataLock.unlock();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.compiler.inspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
public class ChangeSuperClassFix implements LocalQuickFix, HighPriorityAction {
|
||||
@NotNull
|
||||
private final SmartPsiElementPointer<PsiClass> myNewSuperClass;
|
||||
@NotNull
|
||||
private final SmartPsiElementPointer<PsiClass> myOldSuperClass;
|
||||
private final int myInheritorCount;
|
||||
@NotNull
|
||||
private final String myNewSuperName;
|
||||
private final boolean myNewSuperIsInterface;
|
||||
|
||||
public ChangeSuperClassFix(@NotNull final PsiClass newSuperClass, final int percent, @NotNull final PsiClass oldSuperClass) {
|
||||
final SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(newSuperClass.getProject());
|
||||
myNewSuperName = ObjectUtils.notNull(newSuperClass.getQualifiedName());
|
||||
myNewSuperIsInterface = newSuperClass.isInterface();
|
||||
myNewSuperClass = smartPointerManager.createSmartPsiElementPointer(newSuperClass);
|
||||
myOldSuperClass = smartPointerManager.createSmartPsiElementPointer(oldSuperClass);
|
||||
myInheritorCount = percent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@TestOnly
|
||||
public PsiClass getNewSuperClass() {
|
||||
return ObjectUtils.notNull(myNewSuperClass.getElement());
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public int getInheritorCount() {
|
||||
return myInheritorCount;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return String.format("Make " + (myNewSuperIsInterface ? "implements" : "extends") + " '%s'", myNewSuperName);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return GroupNames.INHERITANCE_GROUP_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor problemDescriptor) {
|
||||
final PsiClass oldSuperClass = myOldSuperClass.getElement();
|
||||
final PsiClass newSuperClass = myNewSuperClass.getElement();
|
||||
if (oldSuperClass == null || newSuperClass == null) return;
|
||||
changeSuperClass((PsiClass)problemDescriptor.getPsiElement(), oldSuperClass, newSuperClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* myOldSuperClass and myNewSuperClass can be interfaces or classes in any combination
|
||||
* <p/>
|
||||
* 1. not checks that myOldSuperClass is really super of aClass
|
||||
* 2. not checks that myNewSuperClass not exists in currently existed supers
|
||||
*/
|
||||
private static void changeSuperClass(@NotNull final PsiClass aClass,
|
||||
@NotNull final PsiClass oldSuperClass,
|
||||
@NotNull final PsiClass newSuperClass) {
|
||||
JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(aClass.getProject());
|
||||
PsiElementFactory factory = psiFacade.getElementFactory();
|
||||
if (aClass instanceof PsiAnonymousClass) {
|
||||
((PsiAnonymousClass)aClass).getBaseClassReference().replace(factory.createClassReferenceElement(newSuperClass));
|
||||
return;
|
||||
}
|
||||
PsiReferenceList extendsList = ObjectUtils.notNull(aClass.getExtendsList());
|
||||
PsiJavaCodeReferenceElement[] refElements =
|
||||
ArrayUtil.mergeArrays(getReferences(extendsList), getReferences(aClass.getImplementsList()));
|
||||
for (PsiJavaCodeReferenceElement refElement : refElements) {
|
||||
if (refElement.isReferenceTo(oldSuperClass)) {
|
||||
refElement.delete();
|
||||
}
|
||||
}
|
||||
|
||||
PsiReferenceList list;
|
||||
if (newSuperClass.isInterface()) {
|
||||
list = aClass.getImplementsList();
|
||||
}
|
||||
else {
|
||||
list = extendsList;
|
||||
PsiJavaCodeReferenceElement[] elements = list.getReferenceElements();
|
||||
if (elements.length == 1 &&
|
||||
elements[0].isReferenceTo(psiFacade.findClass(CommonClassNames.JAVA_LANG_OBJECT, aClass.getResolveScope()))) {
|
||||
elements[0].delete();
|
||||
}
|
||||
}
|
||||
PsiElement ref = list.add(factory.createClassReferenceElement(newSuperClass));
|
||||
JavaCodeStyleManager.getInstance(aClass.getProject()).shortenClassReferences(ref);
|
||||
}
|
||||
|
||||
private static PsiJavaCodeReferenceElement[] getReferences(PsiReferenceList list) {
|
||||
return list == null ? PsiJavaCodeReferenceElement.EMPTY_ARRAY : list.getReferenceElements();
|
||||
}
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.compiler.inspection;
|
||||
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.compiler.CompilerReferenceService;
|
||||
import com.intellij.compiler.backwardRefs.CompilerReferenceServiceEx;
|
||||
import com.intellij.compiler.backwardRefs.ReferenceIndexUnavailableException;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaFullClassNameIndex;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import one.util.streamex.MoreCollectors;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.backwardRefs.LightRef;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class FrequentlyUsedInheritorInspection extends BaseJavaLocalInspectionTool {
|
||||
private static final Logger LOG = Logger.getInstance(FrequentlyUsedInheritorInspection.class);
|
||||
|
||||
public static final byte MAX_RESULT = 3;
|
||||
private static final int PERCENT_THRESHOLD = 20;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ProblemDescriptor[] checkClass(@NotNull final PsiClass aClass,
|
||||
@NotNull final InspectionManager manager,
|
||||
final boolean isOnTheFly) {
|
||||
if (aClass.isInterface() || aClass instanceof PsiTypeParameter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final PsiClass superClass = getSuperIfOnlyOne(aClass);
|
||||
if (superClass == null) return null;
|
||||
|
||||
long ms = System.currentTimeMillis();
|
||||
final List<ClassAndInheritorCount> topInheritors = getTopInheritorsUsingCompilerIndices(superClass, aClass.getResolveScope(), aClass);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("search for inheritance structure of " + superClass.getQualifiedName() + " in " + (System.currentTimeMillis() - ms) + " ms");
|
||||
}
|
||||
if (topInheritors.isEmpty()) return null;
|
||||
|
||||
final Collection<LocalQuickFix> topInheritorsQuickFix = new ArrayList<>(topInheritors.size());
|
||||
for (final ClassAndInheritorCount searchResult : topInheritors) {
|
||||
final LocalQuickFix quickFix = new ChangeSuperClassFix(searchResult.psi, searchResult.number, superClass);
|
||||
topInheritorsQuickFix.add(quickFix);
|
||||
if (topInheritorsQuickFix.size() >= MAX_RESULT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new ProblemDescriptor[]{manager
|
||||
.createProblemDescriptor(aClass, "Class can have more common super class", isOnTheFly,
|
||||
topInheritorsQuickFix.toArray(LocalQuickFix.EMPTY_ARRAY),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiClass getSuperIfOnlyOne(@NotNull final PsiClass aClass) {
|
||||
PsiClass superClass = aClass.getSuperClass();
|
||||
if (superClass != null && !CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) {
|
||||
return isInSourceContent(aClass) ? superClass : null;
|
||||
}
|
||||
|
||||
return Arrays
|
||||
.stream(aClass.getInterfaces())
|
||||
.filter(c -> !CommonClassNames.JAVA_LANG_OBJECT.equals(c.getQualifiedName()))
|
||||
.filter(c -> isInSourceContent(c))
|
||||
.collect(MoreCollectors.onlyOne())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<ClassAndInheritorCount> getTopInheritorsUsingCompilerIndices(@NotNull PsiClass aClass,
|
||||
@NotNull GlobalSearchScope searchScope,
|
||||
@NotNull PsiElement place) {
|
||||
String qName = aClass.getQualifiedName();
|
||||
if (qName == null) return Collections.emptyList();
|
||||
|
||||
final Project project = aClass.getProject();
|
||||
final CompilerReferenceServiceEx compilerRefService = (CompilerReferenceServiceEx)CompilerReferenceService.getInstance(project);
|
||||
try {
|
||||
int id = compilerRefService.getNameId(qName);
|
||||
if (id == 0) return Collections.emptyList();
|
||||
return findInheritors(aClass, new LightRef.JavaLightClassRef(id), searchScope, place, -1, project, compilerRefService);
|
||||
}
|
||||
catch (ReferenceIndexUnavailableException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<ClassAndInheritorCount> findInheritors(@NotNull PsiClass aClass,
|
||||
@NotNull LightRef.JavaLightClassRef classAsLightRef,
|
||||
@NotNull GlobalSearchScope searchScope,
|
||||
@NotNull PsiElement place,
|
||||
int hierarchyCardinality,
|
||||
@NotNull Project project,
|
||||
@NotNull CompilerReferenceServiceEx compilerRefService) {
|
||||
LightRef.LightClassHierarchyElementDef[] directInheritors = compilerRefService.getDirectInheritors(classAsLightRef);
|
||||
|
||||
if (hierarchyCardinality == -1) {
|
||||
hierarchyCardinality = compilerRefService.getInheritorCount(classAsLightRef);
|
||||
}
|
||||
int finalHierarchyCardinality = hierarchyCardinality;
|
||||
|
||||
List<ClassAndInheritorCount> directInheritorStats = Stream
|
||||
.of(directInheritors)
|
||||
.filter(inheritor -> !(inheritor instanceof LightRef.LightAnonymousClassDef))
|
||||
.map(inheritor -> {
|
||||
int count = compilerRefService.getInheritorCount(inheritor);
|
||||
if (count * 100 > finalHierarchyCardinality * PERCENT_THRESHOLD) {
|
||||
return new Object() {
|
||||
final LightRef.LightClassHierarchyElementDef myDef = inheritor;
|
||||
final int inheritorCount = count;
|
||||
};
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.map(defAndCount -> {
|
||||
String name = compilerRefService.getName(defAndCount.myDef.getName());
|
||||
PsiClass inheritor =
|
||||
JavaFullClassNameIndex.getInstance().get(name.hashCode(), project, searchScope).stream()
|
||||
.filter(cls -> name.equals(cls.getQualifiedName()))
|
||||
.collect(MoreCollectors.onlyOne())
|
||||
.orElse(null);
|
||||
|
||||
if (inheritor == null || !inheritor.isInheritor(aClass, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ClassAndInheritorCount(inheritor, defAndCount.myDef, defAndCount.inheritorCount);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PsiResolveHelper resolveHelper = PsiResolveHelper.SERVICE.getInstance(project);
|
||||
return directInheritorStats
|
||||
.stream()
|
||||
.filter(c -> resolveHelper.isAccessible(c.psi, place, null))
|
||||
.flatMap(c -> Stream.concat(Stream.of(c), getClassesIfInterface(c, finalHierarchyCardinality, searchScope, place, project, compilerRefService).stream()))
|
||||
.sorted()
|
||||
.limit(MAX_RESULT)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static List<ClassAndInheritorCount> getClassesIfInterface(@NotNull ClassAndInheritorCount classAndInheritorCount,
|
||||
int hierarchyCardinality,
|
||||
GlobalSearchScope searchScope,
|
||||
PsiElement place,
|
||||
Project project,
|
||||
CompilerReferenceServiceEx compilerRefService) {
|
||||
if (classAndInheritorCount.psi.isInterface()) {
|
||||
return findInheritors(classAndInheritorCount.psi,
|
||||
(LightRef.JavaLightClassRef)classAndInheritorCount.descriptor,
|
||||
searchScope,
|
||||
place,
|
||||
hierarchyCardinality,
|
||||
project,
|
||||
compilerRefService);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static boolean isInSourceContent(@NotNull PsiElement e) {
|
||||
final VirtualFile file = e.getContainingFile().getVirtualFile();
|
||||
if (file == null) return false;
|
||||
final ProjectFileIndex index = ProjectRootManager.getInstance(e.getProject()).getFileIndex();
|
||||
return index.isInContent(file);
|
||||
}
|
||||
|
||||
private static class ClassAndInheritorCount implements Comparable<ClassAndInheritorCount> {
|
||||
private final PsiClass psi;
|
||||
private final LightRef.LightClassHierarchyElementDef descriptor;
|
||||
private final int number;
|
||||
|
||||
private ClassAndInheritorCount(PsiClass psi,
|
||||
LightRef.LightClassHierarchyElementDef descriptor,
|
||||
int number) {
|
||||
this.psi = psi;
|
||||
this.descriptor = descriptor;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull ClassAndInheritorCount o) {
|
||||
return - number + o.number;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user