28fc636cbb26be044164d93bf84e095e4a6e0534
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / InheritanceImplUtil.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.psi.impl;
17
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.progress.ProgressIndicatorProvider;
20 import com.intellij.openapi.util.Comparing;
21 import com.intellij.psi.*;
22 import com.intellij.psi.search.GlobalSearchScope;
23 import com.intellij.psi.util.InheritanceUtil;
24 import com.intellij.util.containers.HashSet;
25 import gnu.trove.THashSet;
26 import org.jetbrains.annotations.NonNls;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
29
30 import java.util.Set;
31
32 public class InheritanceImplUtil {
33   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.InheritanceImplUtil");
34
35   public static boolean isInheritor(@NotNull PsiClass candidateClass, @NotNull PsiClass baseClass, final boolean checkDeep) {
36     return !(baseClass instanceof PsiAnonymousClass) && isInheritor(candidateClass, baseClass, checkDeep, null);
37   }
38
39   private static boolean isInheritor(@NotNull PsiClass candidateClass, @NotNull PsiClass baseClass, boolean checkDeep, Set<PsiClass> checkedClasses) {
40     if (candidateClass instanceof PsiAnonymousClass) {
41       final PsiClass baseCandidateClass = ((PsiAnonymousClass)candidateClass).getBaseClassType().resolve();
42       return baseCandidateClass != null && InheritanceUtil.isInheritorOrSelf(baseCandidateClass, baseClass, checkDeep);
43     }
44     PsiManager manager = candidateClass.getManager();
45     /* //TODO fix classhashprovider so it doesn't use class qnames only
46     final ClassHashProvider provider = getHashProvider((PsiManagerImpl) manager);
47     if (checkDeep && provider != null) {
48       try {
49         return provider.isInheritor(baseClass, candidateClass);
50       }
51       catch (ClassHashProvider.OutOfRangeException e) {
52       }
53     }
54     */
55     if(checkDeep && LOG.isDebugEnabled()){
56       LOG.debug("Using uncached version for " + candidateClass.getQualifiedName() + " and " + baseClass);
57     }
58
59     @NonNls final String baseName = baseClass.getName();
60     if ("Object".equals(baseName)) {
61       PsiClass objectClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(CommonClassNames.JAVA_LANG_OBJECT, candidateClass.getResolveScope());
62       if (manager.areElementsEquivalent(baseClass, objectClass)) {
63         if (manager.areElementsEquivalent(candidateClass, objectClass)) return false;
64         if (checkDeep || candidateClass.isInterface()) return true;
65         return manager.areElementsEquivalent(candidateClass.getSuperClass(), objectClass);
66       }
67     }
68
69     if (!checkDeep) {
70       final boolean cInt = candidateClass.isInterface();
71       final boolean bInt = baseClass.isInterface();
72
73       if (candidateClass instanceof PsiCompiledElement) {
74         String baseQName = baseClass.getQualifiedName();
75         if (baseQName == null) return false;
76
77         GlobalSearchScope scope = candidateClass.getResolveScope();
78         if (cInt == bInt && checkReferenceListWithQualifiedNames(baseQName, candidateClass.getExtendsList(), manager, scope)) return true;
79         return bInt && !cInt && checkReferenceListWithQualifiedNames(baseQName, candidateClass.getImplementsList(), manager, scope);
80       }
81       if (cInt == bInt) {
82         for (PsiClassType type : candidateClass.getExtendsListTypes()) {
83           if (Comparing.equal(type.getClassName(), baseName)) {
84             if (manager.areElementsEquivalent(baseClass, type.resolve())) {
85               return true;
86             }
87           }
88         }
89       }
90       else if (!cInt) {
91         for (PsiClassType type : candidateClass.getImplementsListTypes()) {
92           if (Comparing.equal(type.getClassName(), baseName)) {
93             if (manager.areElementsEquivalent(baseClass, type.resolve())) {
94               return true;
95             }
96           }
97         }
98       }
99
100       return false;
101     }
102
103     return isInheritorWithoutCaching(candidateClass, baseClass, checkDeep, checkedClasses);
104   }
105
106   private static boolean checkReferenceListWithQualifiedNames(final String baseQName, final PsiReferenceList extList, final PsiManager manager,
107                                                               final GlobalSearchScope scope) {
108     if (extList != null) {
109       final PsiJavaCodeReferenceElement[] refs = extList.getReferenceElements();
110       for (PsiJavaCodeReferenceElement ref : refs) {
111         if (Comparing.equal(PsiNameHelper.getQualifiedClassName(ref.getQualifiedName(), false), baseQName) && JavaPsiFacade
112           .getInstance(manager.getProject()).findClass(baseQName, scope) != null)
113           return true;
114       }
115     }
116     return false;
117   }
118
119   private static boolean isInheritorWithoutCaching(PsiClass aClass, PsiClass baseClass, boolean checkDeep, Set<PsiClass> checkedClasses) {
120     PsiManager manager = aClass.getManager();
121     if (manager.areElementsEquivalent(aClass, baseClass)) return false;
122
123     if (aClass.isInterface() && !baseClass.isInterface()) {
124       return false;
125     }
126
127     //if (PsiUtil.hasModifierProperty(baseClass, PsiModifier.FINAL)) {
128     //  return false;
129     //}
130
131     if (checkDeep) {
132       if (checkedClasses == null) {
133         checkedClasses = new THashSet<PsiClass>();
134       }
135       checkedClasses.add(aClass);
136     }
137
138     if (!aClass.isInterface() && baseClass.isInterface()) {
139       if (checkDeep && checkInheritor(aClass.getSuperClass(), baseClass, checkDeep, checkedClasses)) {
140         return true;
141       }
142       return checkInheritor(aClass.getInterfaces(), baseClass, checkDeep, checkedClasses);
143
144     }
145     else {
146       return checkInheritor(aClass.getSupers(), baseClass, checkDeep, checkedClasses);
147     }
148   }
149
150   private static boolean checkInheritor(PsiClass[] supers, PsiClass baseClass, boolean checkDeep, Set<PsiClass> checkedClasses) {
151     for (PsiClass aSuper : supers) {
152       if (checkInheritor(aSuper, baseClass, checkDeep, checkedClasses)) {
153         return true;
154       }
155     }
156     return false;
157   }
158
159   private static boolean checkInheritor(PsiClass aClass, PsiClass baseClass, boolean checkDeep, Set<PsiClass> checkedClasses) {
160     ProgressIndicatorProvider.checkCanceled();
161     if (aClass != null) {
162       PsiManager manager = baseClass.getManager();
163       if (manager.areElementsEquivalent(baseClass, aClass)) {
164         return true;
165       }
166       if (checkedClasses != null && checkedClasses.contains(aClass)) { // to prevent infinite recursion
167         return false;
168       }
169       if (checkDeep) {
170         if (isInheritor(aClass, baseClass, checkDeep, checkedClasses)) {
171           return true;
172         }
173       }
174     }
175     return false;
176   }
177
178   public static boolean isInheritorDeep(@NotNull PsiClass candidateClass, @NotNull PsiClass baseClass, @Nullable final PsiClass classToByPass) {
179     if (baseClass instanceof PsiAnonymousClass) {
180       return false;
181     }
182
183     Set<PsiClass> checkedClasses = null;
184     if (classToByPass != null) {
185       checkedClasses = new HashSet<PsiClass>();
186       checkedClasses.add(classToByPass);
187     }
188     return isInheritor(candidateClass, baseClass, true, checkedClasses);
189   }
190 }