2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.psi.impl;
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;
32 public class InheritanceImplUtil {
33 private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.InheritanceImplUtil");
35 public static boolean isInheritor(@NotNull PsiClass candidateClass, @NotNull PsiClass baseClass, final boolean checkDeep) {
36 return !(baseClass instanceof PsiAnonymousClass) && isInheritor(candidateClass, baseClass, checkDeep, null);
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);
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) {
49 return provider.isInheritor(baseClass, candidateClass);
51 catch (ClassHashProvider.OutOfRangeException e) {
55 if(checkDeep && LOG.isDebugEnabled()){
56 LOG.debug("Using uncached version for " + candidateClass.getQualifiedName() + " and " + baseClass);
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);
70 final boolean cInt = candidateClass.isInterface();
71 final boolean bInt = baseClass.isInterface();
73 if (candidateClass instanceof PsiCompiledElement) {
74 String baseQName = baseClass.getQualifiedName();
75 if (baseQName == null) return false;
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);
82 for (PsiClassType type : candidateClass.getExtendsListTypes()) {
83 if (Comparing.equal(type.getClassName(), baseName)) {
84 if (manager.areElementsEquivalent(baseClass, type.resolve())) {
91 for (PsiClassType type : candidateClass.getImplementsListTypes()) {
92 if (Comparing.equal(type.getClassName(), baseName)) {
93 if (manager.areElementsEquivalent(baseClass, type.resolve())) {
103 return isInheritorWithoutCaching(candidateClass, baseClass, checkDeep, checkedClasses);
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)
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;
123 if (aClass.isInterface() && !baseClass.isInterface()) {
127 //if (PsiUtil.hasModifierProperty(baseClass, PsiModifier.FINAL)) {
132 if (checkedClasses == null) {
133 checkedClasses = new THashSet<PsiClass>();
135 checkedClasses.add(aClass);
138 if (!aClass.isInterface() && baseClass.isInterface()) {
139 if (checkDeep && checkInheritor(aClass.getSuperClass(), baseClass, checkDeep, checkedClasses)) {
142 return checkInheritor(aClass.getInterfaces(), baseClass, checkDeep, checkedClasses);
146 return checkInheritor(aClass.getSupers(), baseClass, checkDeep, checkedClasses);
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)) {
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)) {
166 if (checkedClasses != null && checkedClasses.contains(aClass)) { // to prevent infinite recursion
170 if (isInheritor(aClass, baseClass, checkDeep, checkedClasses)) {
178 public static boolean isInheritorDeep(@NotNull PsiClass candidateClass, @NotNull PsiClass baseClass, @Nullable final PsiClass classToByPass) {
179 if (baseClass instanceof PsiAnonymousClass) {
183 Set<PsiClass> checkedClasses = null;
184 if (classToByPass != null) {
185 checkedClasses = new HashSet<PsiClass>();
186 checkedClasses.add(classToByPass);
188 return isInheritor(candidateClass, baseClass, true, checkedClasses);