accept that with different library versions inheritance relation may be intransitive...
[idea/community.git] / java / java-indexing-impl / src / com / intellij / psi / impl / search / JavaOverridingMethodsSearcher.java
1 package com.intellij.psi.impl.search;\r
2 \r
3 import com.intellij.openapi.application.ApplicationManager;\r
4 import com.intellij.openapi.util.Computable;\r
5 import com.intellij.psi.*;\r
6 import com.intellij.psi.search.SearchScope;\r
7 import com.intellij.psi.search.searches.ClassInheritorsSearch;\r
8 import com.intellij.psi.search.searches.OverridingMethodsSearch;\r
9 import com.intellij.psi.util.MethodSignature;\r
10 import com.intellij.psi.util.MethodSignatureUtil;\r
11 import com.intellij.psi.util.TypeConversionUtil;\r
12 import com.intellij.util.Processor;\r
13 import com.intellij.util.QueryExecutor;\r
14 import org.jetbrains.annotations.NotNull;\r
15 import org.jetbrains.annotations.Nullable;\r
16 \r
17 /**\r
18  * @author max\r
19  */\r
20 public class JavaOverridingMethodsSearcher implements QueryExecutor<PsiMethod, OverridingMethodsSearch.SearchParameters> {\r
21   @Override\r
22   public boolean execute(@NotNull final OverridingMethodsSearch.SearchParameters p, @NotNull final Processor<PsiMethod> consumer) {\r
23     final PsiMethod method = p.getMethod();\r
24     final SearchScope scope = p.getScope();\r
25 \r
26     final PsiClass parentClass = ApplicationManager.getApplication().runReadAction(new Computable<PsiClass>() {\r
27       @Nullable\r
28       @Override\r
29       public PsiClass compute() {\r
30         return method.getContainingClass();\r
31       }\r
32     });\r
33     assert parentClass != null;\r
34     Processor<PsiClass> inheritorsProcessor = new Processor<PsiClass>() {\r
35       @Override\r
36       public boolean process(final PsiClass inheritor) {\r
37         PsiMethod found = ApplicationManager.getApplication().runReadAction(new Computable<PsiMethod>() {\r
38           @Override\r
39           @Nullable\r
40           public PsiMethod compute() {\r
41             return findOverridingMethod(inheritor, parentClass, method);\r
42           }\r
43         });\r
44         return found == null || consumer.process(found) && p.isCheckDeep();\r
45       }\r
46     };\r
47 \r
48     return ClassInheritorsSearch.search(parentClass, scope, true).forEach(inheritorsProcessor);\r
49   }\r
50 \r
51   @Nullable\r
52   private static PsiMethod findOverridingMethod(PsiClass inheritor, @NotNull PsiClass parentClass, PsiMethod method) {\r
53     if (!inheritor.isInheritor(parentClass, true)) {\r
54       return null;\r
55     }\r
56 \r
57     PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(parentClass, inheritor, PsiSubstitutor.EMPTY);\r
58     MethodSignature signature = method.getSignature(substitutor);\r
59     PsiMethod found = MethodSignatureUtil.findMethodBySuperSignature(inheritor, signature, false);\r
60     if (found != null && isAcceptable(found, method)) {\r
61       return found;\r
62     }\r
63 \r
64     if (parentClass.isInterface() && !inheritor.isInterface()) {  //check for sibling implementation\r
65       final PsiClass superClass = inheritor.getSuperClass();\r
66       if (superClass != null && !superClass.isInheritor(parentClass, true)) {\r
67         PsiMethod derived = MethodSignatureUtil.findMethodInSuperClassBySignatureInDerived(inheritor, superClass, signature, true);\r
68         if (derived != null && isAcceptable(derived, method)) {\r
69           return derived;\r
70         }\r
71       }\r
72     }\r
73     return null;\r
74   }\r
75 \r
76   private static boolean isAcceptable(final PsiMethod found, final PsiMethod method) {\r
77     return !found.hasModifierProperty(PsiModifier.STATIC) &&\r
78            (!method.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) ||\r
79             JavaPsiFacade.getInstance(found.getProject())\r
80               .arePackagesTheSame(method.getContainingClass(), found.getContainingClass()));\r
81   }\r
82 }\r