diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2013-01-08 11:11:20 -0800 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2013-01-08 11:11:20 -0800 |
commit | b56ea2a18f232d79481e778085fd64e8ae486fc3 (patch) | |
tree | 44e1f6eb4864a45033f865b74fe783e3d784dd6a /java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java | |
download | idea-b56ea2a18f232d79481e778085fd64e8ae486fc3.tar.gz |
Snapshot of commit d5ec1d5018ed24f1b4f32b1d09df6dbd7e2fc425
from branch master of git://git.jetbrains.org/idea/community.git
Diffstat (limited to 'java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java')
-rw-r--r-- | java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java | 1111 |
1 files changed, 1111 insertions, 0 deletions
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java new file mode 100644 index 000000000000..8387b8cc35d0 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java @@ -0,0 +1,1111 @@ +/* + * Copyright 2000-2011 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; + +import com.intellij.lang.ASTNode; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressIndicatorProvider; +import com.intellij.openapi.roots.FileIndexFacade; +import com.intellij.openapi.util.*; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; +import com.intellij.psi.filters.OrFilter; +import com.intellij.psi.impl.compiled.ClsElementImpl; +import com.intellij.psi.impl.source.PsiImmediateClassType; +import com.intellij.psi.infos.MethodCandidateInfo; +import com.intellij.psi.scope.ElementClassFilter; +import com.intellij.psi.scope.ElementClassHint; +import com.intellij.psi.scope.NameHint; +import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.scope.processor.FilterScopeProcessor; +import com.intellij.psi.scope.processor.MethodResolverProcessor; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.PackageScope; +import com.intellij.psi.search.SearchScope; +import com.intellij.psi.util.*; +import com.intellij.ui.IconDeferrer; +import com.intellij.ui.RowIcon; +import com.intellij.util.Function; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.NullableFunction; +import com.intellij.util.SmartList; +import com.intellij.util.containers.HashSet; +import gnu.trove.THashMap; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.*; + +/** + * @author ik + * Date: 24.10.2003 + */ +public class PsiClassImplUtil { + private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiClassImplUtil"); + private static final Key<ParameterizedCachedValue<MembersMap, PsiClass>> MAP_IN_CLASS_KEY = Key.create("MAP_KEY"); + + private PsiClassImplUtil() { + } + + public static void cacheEverything(PsiClass aClass) { + getValues(aClass).getValue(aClass); + } + + @NotNull + public static PsiField[] getAllFields(@NotNull PsiClass aClass) { + List<PsiField> map = getAllByMap(aClass, MemberType.FIELD); + return map.toArray(new PsiField[map.size()]); + } + + @NotNull + public static PsiMethod[] getAllMethods(@NotNull PsiClass aClass) { + List<PsiMethod> methods = getAllByMap(aClass, MemberType.METHOD); + return methods.toArray(new PsiMethod[methods.size()]); + } + + @NotNull + public static PsiClass[] getAllInnerClasses(@NotNull PsiClass aClass) { + List<PsiClass> classes = getAllByMap(aClass, MemberType.CLASS); + return classes.toArray(new PsiClass[classes.size()]); + } + + @Nullable + public static PsiField findFieldByName(@NotNull PsiClass aClass, String name, boolean checkBases) { + List<PsiMember> byMap = findByMap(aClass, name, checkBases, MemberType.FIELD); + return byMap.isEmpty() ? null : (PsiField)byMap.get(0); + } + + @NotNull + public static PsiMethod[] findMethodsByName(@NotNull PsiClass aClass, String name, boolean checkBases) { + List<PsiMember> methods = findByMap(aClass, name, checkBases, MemberType.METHOD); + //noinspection SuspiciousToArrayCall + return methods.toArray(new PsiMethod[methods.size()]); + } + + @Nullable + public static PsiMethod findMethodBySignature(@NotNull PsiClass aClass, @NotNull PsiMethod patternMethod, final boolean checkBases) { + final List<PsiMethod> result = findMethodsBySignature(aClass, patternMethod, checkBases, true); + return result.isEmpty() ? null : result.get(0); + } + + // ----------------------------- findMethodsBySignature ----------------------------------- + + @NotNull + public static PsiMethod[] findMethodsBySignature(@NotNull PsiClass aClass, @NotNull PsiMethod patternMethod, final boolean checkBases) { + List<PsiMethod> methods = findMethodsBySignature(aClass, patternMethod, checkBases, false); + return methods.toArray(new PsiMethod[methods.size()]); + } + + @NotNull + private static List<PsiMethod> findMethodsBySignature(@NotNull PsiClass aClass, + @NotNull PsiMethod patternMethod, + boolean checkBases, + boolean stopOnFirst) { + final PsiMethod[] methodsByName = aClass.findMethodsByName(patternMethod.getName(), checkBases); + if (methodsByName.length == 0) return Collections.emptyList(); + final List<PsiMethod> methods = new SmartList<PsiMethod>(); + final MethodSignature patternSignature = patternMethod.getSignature(PsiSubstitutor.EMPTY); + for (final PsiMethod method : methodsByName) { + final PsiClass superClass = method.getContainingClass(); + final PsiSubstitutor substitutor; + if (checkBases && !aClass.equals(superClass)) { + substitutor = TypeConversionUtil.getSuperClassSubstitutor(superClass, aClass, PsiSubstitutor.EMPTY); + } + else { + substitutor = PsiSubstitutor.EMPTY; + } + final MethodSignature signature = method.getSignature(substitutor); + if (signature.equals(patternSignature)) { + methods.add(method); + if (stopOnFirst) { + break; + } + } + } + return methods; + } + + // ---------------------------------------------------------------------------------------- + + @Nullable + public static PsiClass findInnerByName(@NotNull PsiClass aClass, String name, boolean checkBases) { + List<PsiMember> byMap = findByMap(aClass, name, checkBases, MemberType.CLASS); + return byMap.isEmpty() ? null : (PsiClass)byMap.get(0); + } + + @NotNull + private static List<PsiMember> findByMap(@NotNull PsiClass aClass, String name, boolean checkBases, @NotNull MemberType type) { + if (name == null) return Collections.emptyList(); + + if (checkBases) { + Map<String, List<Pair<PsiMember, PsiSubstitutor>>> allMethodsMap = getMap(aClass, type); + List<Pair<PsiMember, PsiSubstitutor>> list = allMethodsMap.get(name); + if (list == null) return Collections.emptyList(); + List<PsiMember> ret = new ArrayList<PsiMember>(list.size()); + for (final Pair<PsiMember, PsiSubstitutor> info : list) { + ret.add(info.getFirst()); + } + + return ret; + } + else { + PsiMember[] members = null; + switch (type) { + case METHOD: + members = aClass.getMethods(); + break; + case CLASS: + members = aClass.getInnerClasses(); + break; + case FIELD: + members = aClass.getFields(); + break; + } + + List<PsiMember> list = new ArrayList<PsiMember>(); + for (PsiMember member : members) { + if (name.equals(member.getName())) { + list.add(member); + } + } + return list; + } + } + + @NotNull + public static <T extends PsiMember> List<Pair<T, PsiSubstitutor>> getAllWithSubstitutorsByMap(@NotNull PsiClass aClass, @NotNull MemberType type) { + Map<String, List<Pair<PsiMember, PsiSubstitutor>>> allMap = getMap(aClass, type); + //noinspection unchecked + return (List)allMap.get(ALL); + } + + @NotNull + private static <T extends PsiMember> List<T> getAllByMap(@NotNull PsiClass aClass, @NotNull MemberType type) { + List<Pair<T, PsiSubstitutor>> pairs = getAllWithSubstitutorsByMap(aClass, type); + + final List<T> ret = new ArrayList<T>(pairs.size()); + //noinspection ForLoopReplaceableByForEach + for (int i = 0; i < pairs.size(); i++) { + Pair<T, PsiSubstitutor> pair = pairs.get(i); + T t = pair.getFirst(); + LOG.assertTrue(t != null, aClass); + ret.add(t); + } + return ret; + } + + @NonNls private static final String ALL = "Intellij-IDEA-ALL"; + + public enum MemberType {CLASS, FIELD, METHOD} + + @NotNull + private static MembersMap buildAllMaps(@NotNull PsiClass psiClass) { + final List<Pair<PsiMember, PsiSubstitutor>> classes = new ArrayList<Pair<PsiMember, PsiSubstitutor>>(); + final List<Pair<PsiMember, PsiSubstitutor>> fields = new ArrayList<Pair<PsiMember, PsiSubstitutor>>(); + final List<Pair<PsiMember, PsiSubstitutor>> methods = new ArrayList<Pair<PsiMember, PsiSubstitutor>>(); + + FilterScopeProcessor<MethodCandidateInfo> processor = new FilterScopeProcessor<MethodCandidateInfo>( + new OrFilter(ElementClassFilter.METHOD, ElementClassFilter.FIELD, ElementClassFilter.CLASS)) { + @Override + protected void add(PsiElement element, PsiSubstitutor substitutor) { + if (element instanceof PsiMethod) { + methods.add(Pair.create((PsiMember)element, substitutor)); + } + else if (element instanceof PsiField) { + fields.add(Pair.create((PsiMember)element, substitutor)); + } + else if (element instanceof PsiClass) { + classes.add(Pair.create((PsiMember)element, substitutor)); + } + } + }; + processDeclarationsInClassNotCached(psiClass, processor, ResolveState.initial(), null, null, psiClass, false, + PsiUtil.getLanguageLevel(psiClass)); + + MembersMap result = new MembersMap(MemberType.class); + result.put(MemberType.CLASS, generateMapByList(classes)); + result.put(MemberType.METHOD, generateMapByList(methods)); + result.put(MemberType.FIELD, generateMapByList(fields)); + return result; + } + + @NotNull + private static Map<String, List<Pair<PsiMember, PsiSubstitutor>>> generateMapByList(@NotNull final List<Pair<PsiMember, PsiSubstitutor>> list) { + Map<String, List<Pair<PsiMember, PsiSubstitutor>>> map = new THashMap<String, List<Pair<PsiMember, PsiSubstitutor>>>(); + map.put(ALL, list); + for (final Pair<PsiMember, PsiSubstitutor> info : list) { + PsiMember element = info.getFirst(); + String currentName = element.getName(); + List<Pair<PsiMember, PsiSubstitutor>> listByName = map.get(currentName); + if (listByName == null) { + listByName = new ArrayList<Pair<PsiMember, PsiSubstitutor>>(1); + map.put(currentName, listByName); + } + listByName.add(info); + } + return map; + } + + private static Map<String, List<Pair<PsiMember, PsiSubstitutor>>> getMap(@NotNull PsiClass aClass, @NotNull MemberType type) { + ParameterizedCachedValue<MembersMap, PsiClass> value = getValues(aClass); + return value.getValue(aClass).get(type); + } + + @NotNull + private static ParameterizedCachedValue<MembersMap, PsiClass> getValues(@NotNull PsiClass aClass) { + ParameterizedCachedValue<MembersMap, PsiClass> value = aClass.getUserData(MAP_IN_CLASS_KEY); + if (value == null) { + value = CachedValuesManager.getManager(aClass.getProject()).createParameterizedCachedValue(ByNameCachedValueProvider.INSTANCE, false); + //Do not cache for nonphysical elements + if (aClass.isPhysical()) { + value = ((UserDataHolderEx)aClass).putUserDataIfAbsent(MAP_IN_CLASS_KEY, value); + } + } + return value; + } + + private static class ClassIconRequest { + @NotNull private final PsiClass psiClass; + private final int flags; + private final Icon symbolIcon; + + private ClassIconRequest(@NotNull PsiClass psiClass, int flags, Icon symbolIcon) { + this.psiClass = psiClass; + this.flags = flags; + this.symbolIcon = symbolIcon; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ClassIconRequest)) return false; + + ClassIconRequest that = (ClassIconRequest)o; + + return flags == that.flags && psiClass.equals(that.psiClass); + } + + @Override + public int hashCode() { + int result = psiClass.hashCode(); + result = 31 * result + flags; + return result; + } + } + + private static final Function<ClassIconRequest, Icon> FULL_ICON_EVALUATOR = new NullableFunction<ClassIconRequest, Icon>() { + @Override + public Icon fun(ClassIconRequest r) { + if (!r.psiClass.isValid() || r.psiClass.getProject().isDisposed()) return null; + + final boolean isLocked = (r.flags & Iconable.ICON_FLAG_READ_STATUS) != 0 && !r.psiClass.isWritable(); + Icon symbolIcon = r.symbolIcon != null + ? r.symbolIcon + : ElementPresentationUtil.getClassIconOfKind(r.psiClass, ElementPresentationUtil.getClassKind(r.psiClass)); + RowIcon baseIcon = ElementPresentationUtil.createLayeredIcon(symbolIcon, r.psiClass, isLocked); + return ElementPresentationUtil.addVisibilityIcon(r.psiClass, r.flags, baseIcon); + } + }; + + public static Icon getClassIcon(final int flags, @NotNull PsiClass aClass) { + return getClassIcon(flags, aClass, null); + } + + public static Icon getClassIcon(int flags, @NotNull PsiClass aClass, @Nullable Icon symbolIcon) { + Icon base = Iconable.LastComputedIcon.get(aClass, flags); + if (base == null) { + if (symbolIcon == null) { + symbolIcon = ElementPresentationUtil.getClassIconOfKind(aClass, ElementPresentationUtil.getBasicClassKind(aClass)); + } + RowIcon baseIcon = ElementBase.createLayeredIcon(aClass, symbolIcon, 0); + base = ElementPresentationUtil.addVisibilityIcon(aClass, flags, baseIcon); + } + + return IconDeferrer.getInstance().defer(base, new ClassIconRequest(aClass, flags, symbolIcon), FULL_ICON_EVALUATOR); + } + + @NotNull + public static SearchScope getClassUseScope(@NotNull PsiClass aClass) { + if (aClass instanceof PsiAnonymousClass) { + return new LocalSearchScope(aClass); + } + final GlobalSearchScope maximalUseScope = ResolveScopeManager.getElementUseScope(aClass); + PsiFile file = aClass.getContainingFile(); + if (PsiImplUtil.isInServerPage(file)) return maximalUseScope; + final PsiClass containingClass = aClass.getContainingClass(); + if (aClass.hasModifierProperty(PsiModifier.PUBLIC) || + aClass.hasModifierProperty(PsiModifier.PROTECTED)) { + return containingClass == null ? maximalUseScope : containingClass.getUseScope(); + } + else if (aClass.hasModifierProperty(PsiModifier.PRIVATE) || aClass instanceof PsiTypeParameter) { + PsiClass topClass = PsiUtil.getTopLevelClass(aClass); + return new LocalSearchScope(topClass == null ? aClass.getContainingFile() : topClass); + } + else { + PsiPackage aPackage = null; + if (file instanceof PsiJavaFile) { + aPackage = JavaPsiFacade.getInstance(aClass.getProject()).findPackage(((PsiJavaFile)file).getPackageName()); + } + + if (aPackage == null) { + PsiDirectory dir = file.getContainingDirectory(); + if (dir != null) { + aPackage = JavaDirectoryService.getInstance().getPackage(dir); + } + } + + if (aPackage != null) { + SearchScope scope = PackageScope.packageScope(aPackage, false); + scope = scope.intersectWith(maximalUseScope); + return scope; + } + + return new LocalSearchScope(file); + } + } + + public static boolean isMainOrPremainMethod(@NotNull PsiMethod method) { + if (!PsiType.VOID.equals(method.getReturnType())) return false; + String name = method.getName(); + if (!("main".equals(name) || "premain".equals(name))) return false; + + PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory(); + MethodSignature signature = method.getSignature(PsiSubstitutor.EMPTY); + try { + MethodSignature main = createSignatureFromText(factory, "void main(String[] args);"); + if (MethodSignatureUtil.areSignaturesEqual(signature, main)) return true; + MethodSignature premain = createSignatureFromText(factory, "void premain(String args, java.lang.instrument.Instrumentation i);"); + if (MethodSignatureUtil.areSignaturesEqual(signature, premain)) return true; + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + + return false; + } + + @NotNull + private static MethodSignature createSignatureFromText(@NotNull PsiElementFactory factory, @NotNull String text) { + return factory.createMethodFromText(text, null).getSignature(PsiSubstitutor.EMPTY); + } + + private static class MembersMap extends EnumMap<MemberType, Map<String, List<Pair<PsiMember, PsiSubstitutor>>>> { + public MembersMap(@NotNull Class<MemberType> keyType) { + super(keyType); + } + } + + private static class ByNameCachedValueProvider implements ParameterizedCachedValueProvider<MembersMap, PsiClass> { + private static final ByNameCachedValueProvider INSTANCE = new ByNameCachedValueProvider(); + + @Override + public CachedValueProvider.Result<MembersMap> compute(@NotNull PsiClass myClass) { + MembersMap map = buildAllMaps(myClass); + return new CachedValueProvider.Result<MembersMap>(map, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); + } + } + + public static boolean processDeclarationsInClass(@NotNull PsiClass aClass, + @NotNull final PsiScopeProcessor processor, + @NotNull ResolveState state, + @Nullable Set<PsiClass> visited, + PsiElement last, + @NotNull PsiElement place, + boolean isRaw) { + if (last instanceof PsiTypeParameterList || last instanceof PsiModifierList) { + return true; //TypeParameterList and ModifierList do not see our declarations + } + if (visited != null && visited.contains(aClass)) return true; + + PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY); + isRaw = isRaw || PsiUtil.isRawSubstitutor(aClass, substitutor); + + ParameterizedCachedValue<MembersMap, PsiClass> cache = getValues(aClass); //aClass.getUserData(MAP_IN_CLASS_KEY); + boolean upToDate = cache.hasUpToDateValue(); + LanguageLevel languageLevel = PsiUtil.getLanguageLevel(place); + if (/*true || */upToDate) { + final NameHint nameHint = processor.getHint(NameHint.KEY); + if (nameHint != null) { + String name = nameHint.getName(state); + return processCachedMembersByName(aClass, processor, state, visited, last, place, isRaw, substitutor, cache.getValue(aClass), name,languageLevel); + } + } + return processDeclarationsInClassNotCached(aClass, processor, state, visited, last, place, isRaw, languageLevel); + } + + private static boolean processCachedMembersByName(@NotNull PsiClass aClass, + @NotNull PsiScopeProcessor processor, + @NotNull ResolveState state, + @Nullable Set<PsiClass> visited, + PsiElement last, + @NotNull PsiElement place, + boolean isRaw, + @NotNull PsiSubstitutor substitutor, + @NotNull MembersMap value, + String name, + @NotNull LanguageLevel languageLevel) { + final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY); + + PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory(); + + if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.FIELD)) { + final PsiField fieldByName = aClass.findFieldByName(name, false); + if (fieldByName != null) { + processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass); + if (!processor.execute(fieldByName, state)) return false; + } + else { + final Map<String, List<Pair<PsiMember, PsiSubstitutor>>> allFieldsMap = value.get(MemberType.FIELD); + + final List<Pair<PsiMember, PsiSubstitutor>> list = allFieldsMap.get(name); + if (list != null) { + for (final Pair<PsiMember, PsiSubstitutor> candidate : list) { + PsiMember candidateField = candidate.getFirst(); + PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(candidateField.getContainingClass(), candidate.getSecond(), aClass, + substitutor, factory, languageLevel); + + processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, candidateField.getContainingClass()); + if (!processor.execute(candidateField, state.put(PsiSubstitutor.KEY, finalSubstitutor))) return false; + } + } + } + } + if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) { + if (last != null && last.getContext() == aClass) { + if (last instanceof PsiClass) { + if (!processor.execute(last, state)) return false; + } + // Parameters + final PsiTypeParameterList list = aClass.getTypeParameterList(); + if (list != null && !list.processDeclarations(processor, state, last, place)) return false; + } + if (!(last instanceof PsiReferenceList)) { + final PsiClass classByName = aClass.findInnerClassByName(name, false); + if (classByName != null) { + processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass); + if (!processor.execute(classByName, state)) return false; + } + else { + Map<String, List<Pair<PsiMember, PsiSubstitutor>>> allClassesMap = value.get(MemberType.CLASS); + + List<Pair<PsiMember, PsiSubstitutor>> list = allClassesMap.get(name); + if (list != null) { + for (final Pair<PsiMember, PsiSubstitutor> candidate : list) { + PsiMember inner = candidate.getFirst(); + PsiClass containingClass = inner.getContainingClass(); + if (containingClass != null) { + PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(containingClass, candidate.getSecond(), aClass, + substitutor, factory, languageLevel); + processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass); + if (!processor.execute(inner, state.put(PsiSubstitutor.KEY, finalSubstitutor))) return false; + } + } + } + } + } + } + if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.METHOD)) { + if (processor instanceof MethodResolverProcessor) { + final MethodResolverProcessor methodResolverProcessor = (MethodResolverProcessor)processor; + if (methodResolverProcessor.isConstructor()) { + final PsiMethod[] constructors = aClass.getConstructors(); + methodResolverProcessor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass); + for (PsiMethod constructor : constructors) { + if (!methodResolverProcessor.execute(constructor, state)) return false; + } + return true; + } + } + Map<String, List<Pair<PsiMember, PsiSubstitutor>>> allMethodsMap = value.get(MemberType.METHOD); + List<Pair<PsiMember, PsiSubstitutor>> list = allMethodsMap.get(name); + if (list != null) { + for (final Pair<PsiMember, PsiSubstitutor> candidate : list) { + ProgressIndicatorProvider.checkCanceled(); + PsiMethod candidateMethod = (PsiMethod)candidate.getFirst(); + if (processor instanceof MethodResolverProcessor) { + if (candidateMethod.isConstructor() != ((MethodResolverProcessor)processor).isConstructor()) continue; + } + final PsiClass containingClass = candidateMethod.getContainingClass(); + if (visited != null && visited.contains(candidateMethod.getContainingClass())) { + continue; + } + + PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(containingClass, candidate.getSecond(), aClass, + substitutor, factory, languageLevel); + finalSubstitutor = checkRaw(isRaw, factory, candidateMethod, finalSubstitutor); + processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass); + if (!processor.execute(candidateMethod, state.put(PsiSubstitutor.KEY, finalSubstitutor))) return false; + } + + if (visited != null) { + for (Pair<PsiMember, PsiSubstitutor> aList : list) { + visited.add(aList.getFirst().getContainingClass()); + } + } + } + } + return true; + } + + private static PsiSubstitutor checkRaw(boolean isRaw, + @NotNull PsiElementFactory factory, + @NotNull PsiMethod candidateMethod, + @NotNull PsiSubstitutor substitutor) { + if (isRaw && !candidateMethod.hasModifierProperty(PsiModifier.STATIC)) { //static methods are not erased due to raw overriding + PsiTypeParameter[] methodTypeParameters = candidateMethod.getTypeParameters(); + substitutor = factory.createRawSubstitutor(substitutor, methodTypeParameters); + } + return substitutor; + } + + public static PsiSubstitutor obtainFinalSubstitutor(@NotNull PsiClass candidateClass, + @NotNull PsiSubstitutor candidateSubstitutor, + @NotNull PsiClass aClass, + @NotNull PsiSubstitutor substitutor, + @NotNull PsiElementFactory elementFactory, + @NotNull LanguageLevel languageLevel) { + if (PsiUtil.isRawSubstitutor(aClass, substitutor)) { + return elementFactory.createRawSubstitutor(candidateClass).putAll(substitutor); + } + final PsiType containingType = elementFactory.createType(candidateClass, candidateSubstitutor, languageLevel); + PsiType type = substitutor.substitute(containingType); + if (!(type instanceof PsiClassType)) return candidateSubstitutor; + return ((PsiClassType)type).resolveGenerics().getSubstitutor(); + } + + private static boolean processDeclarationsInClassNotCached(@NotNull PsiClass aClass, + @NotNull PsiScopeProcessor processor, + @NotNull ResolveState state, + @Nullable Set<PsiClass> visited, + PsiElement last, + @NotNull PsiElement place, + boolean isRaw, + @NotNull LanguageLevel languageLevel) { + if (visited == null) visited = new THashSet<PsiClass>(); + if (!visited.add(aClass)) return true; + processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass); + final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY); + final NameHint nameHint = processor.getHint(NameHint.KEY); + + + if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.FIELD)) { + if (nameHint != null) { + final PsiField fieldByName = aClass.findFieldByName(nameHint.getName(state), false); + if (fieldByName != null && !processor.execute(fieldByName, state)) return false; + } + else { + final PsiField[] fields = aClass.getFields(); + for (final PsiField field : fields) { + if (!processor.execute(field, state)) return false; + } + } + } + + PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory(); + + if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.METHOD)) { + PsiSubstitutor baseSubstitutor = state.get(PsiSubstitutor.KEY); + final PsiMethod[] methods = nameHint != null ? aClass.findMethodsByName(nameHint.getName(state), false) : aClass.getMethods(); + for (final PsiMethod method : methods) { + PsiSubstitutor finalSubstitutor = checkRaw(isRaw, factory, method, baseSubstitutor); + ResolveState methodState = finalSubstitutor == baseSubstitutor ? state : state.put(PsiSubstitutor.KEY, finalSubstitutor); + if (!processor.execute(method, methodState)) return false; + } + } + + if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) { + if (last != null && last.getContext() == aClass) { + // Parameters + final PsiTypeParameterList list = aClass.getTypeParameterList(); + if (list != null && !list.processDeclarations(processor, ResolveState.initial(), last, place)) return false; + } + + if (!(last instanceof PsiReferenceList) && !(last instanceof PsiModifierList)) { + // Inners + if (nameHint != null) { + final PsiClass inner = aClass.findInnerClassByName(nameHint.getName(state), false); + if (inner != null) { + if (!processor.execute(inner, state)) return false; + } + } + else { + final PsiClass[] inners = aClass.getInnerClasses(); + for (final PsiClass inner : inners) { + if (!processor.execute(inner, state)) return false; + } + } + } + } + + return last instanceof PsiReferenceList || processSuperTypes(aClass, processor, visited, last, place, state, isRaw, factory, + languageLevel); + } + + private static boolean processSuperTypes(@NotNull PsiClass aClass, + @NotNull PsiScopeProcessor processor, + @Nullable Set<PsiClass> visited, + PsiElement last, + @NotNull PsiElement place, + @NotNull ResolveState state, + boolean isRaw, + @NotNull PsiElementFactory factory, + @NotNull LanguageLevel languageLevel) { + boolean resolved = false; + for (final PsiClassType superType : aClass.getSuperTypes()) { + final PsiClassType.ClassResolveResult superTypeResolveResult = superType.resolveGenerics(); + PsiClass superClass = superTypeResolveResult.getElement(); + if (superClass == null) continue; + PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(superClass, superTypeResolveResult.getSubstitutor(), aClass, + state.get(PsiSubstitutor.KEY), factory, languageLevel); + if (!processDeclarationsInClass(superClass, processor, state.put(PsiSubstitutor.KEY, finalSubstitutor), visited, last, place, isRaw)) { + resolved = true; + } + } + return !resolved; + } + + @Nullable + public static PsiClass getSuperClass(@NotNull PsiClass psiClass) { + PsiManager manager = psiClass.getManager(); + GlobalSearchScope resolveScope = psiClass.getResolveScope(); + + final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject()); + if (psiClass.isInterface()) { + return facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope); + } + if (psiClass.isEnum()) { + return facade.findClass(CommonClassNames.JAVA_LANG_ENUM, resolveScope); + } + + if (psiClass instanceof PsiAnonymousClass) { + PsiClassType baseClassReference = ((PsiAnonymousClass)psiClass).getBaseClassType(); + PsiClass baseClass = baseClassReference.resolve(); + if (baseClass == null || baseClass.isInterface()) return facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope); + return baseClass; + } + + if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) return null; + + final PsiClassType[] referenceElements = psiClass.getExtendsListTypes(); + + if (referenceElements.length == 0) return facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope); + + PsiClass psiResoved = referenceElements[0].resolve(); + return psiResoved == null ? facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope) : psiResoved; + } + + @NotNull + public static PsiClass[] getSupers(@NotNull PsiClass psiClass) { + final PsiClass[] supers = getSupersInner(psiClass); + for (final PsiClass aSuper : supers) { + LOG.assertTrue(aSuper != null); + } + return supers; + } + + @NotNull + private static PsiClass[] getSupersInner(@NotNull PsiClass psiClass) { + PsiClassType[] extendsListTypes = psiClass.getExtendsListTypes(); + PsiClassType[] implementsListTypes = psiClass.getImplementsListTypes(); + + if (psiClass.isInterface()) { + return resolveClassReferenceList(extendsListTypes, psiClass.getManager(), psiClass.getResolveScope(), true); + } + + if (psiClass instanceof PsiAnonymousClass) { + PsiAnonymousClass psiAnonymousClass = (PsiAnonymousClass)psiClass; + PsiClassType baseClassReference = psiAnonymousClass.getBaseClassType(); + PsiClass baseClass = baseClassReference.resolve(); + if (baseClass != null) { + if (baseClass.isInterface()) { + PsiClass objectClass = + JavaPsiFacade.getInstance(psiClass.getProject()).findClass(CommonClassNames.JAVA_LANG_OBJECT, psiClass.getResolveScope()); + return objectClass != null ? new PsiClass[]{objectClass, baseClass} : new PsiClass[]{baseClass}; + } + return new PsiClass[]{baseClass}; + } + + PsiClass objectClass = + JavaPsiFacade.getInstance(psiClass.getProject()).findClass(CommonClassNames.JAVA_LANG_OBJECT, psiClass.getResolveScope()); + return objectClass != null ? new PsiClass[]{objectClass} : PsiClass.EMPTY_ARRAY; + } + if (psiClass instanceof PsiTypeParameter) { + if (extendsListTypes.length == 0) { + final PsiClass objectClass = + JavaPsiFacade.getInstance(psiClass.getProject()).findClass(CommonClassNames.JAVA_LANG_OBJECT, psiClass.getResolveScope()); + return objectClass != null ? new PsiClass[]{objectClass} : PsiClass.EMPTY_ARRAY; + } + return resolveClassReferenceList(extendsListTypes, psiClass.getManager(), psiClass.getResolveScope(), false); + } + + PsiClass[] interfaces = resolveClassReferenceList(implementsListTypes, psiClass.getManager(), psiClass.getResolveScope(), false); + + PsiClass superClass = getSuperClass(psiClass); + if (superClass == null) return interfaces; + PsiClass[] types = new PsiClass[interfaces.length + 1]; + types[0] = superClass; + System.arraycopy(interfaces, 0, types, 1, interfaces.length); + + return types; + } + + @NotNull + public static PsiClassType[] getSuperTypes(@NotNull PsiClass psiClass) { + if (psiClass instanceof PsiAnonymousClass) { + PsiClassType baseClassType = ((PsiAnonymousClass)psiClass).getBaseClassType(); + PsiClass baseClass = baseClassType.resolve(); + if (baseClass == null || !baseClass.isInterface()) { + return new PsiClassType[]{baseClassType}; + } + else { + PsiClassType objectType = PsiType.getJavaLangObject(psiClass.getManager(), psiClass.getResolveScope()); + return new PsiClassType[]{objectType, baseClassType}; + } + } + + PsiClassType[] extendsTypes = psiClass.getExtendsListTypes(); + PsiClassType[] implementsTypes = psiClass.getImplementsListTypes(); + boolean hasExtends = extendsTypes.length != 0; + int extendsListLength = extendsTypes.length + (hasExtends ? 0 : 1); + PsiClassType[] result = new PsiClassType[extendsListLength + implementsTypes.length]; + + System.arraycopy(extendsTypes, 0, result, 0, extendsTypes.length); + if (!hasExtends) { + if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) { + return PsiClassType.EMPTY_ARRAY; + } + PsiManager manager = psiClass.getManager(); + PsiClassType objectType = PsiType.getJavaLangObject(manager, psiClass.getResolveScope()); + result[0] = objectType; + } + System.arraycopy(implementsTypes, 0, result, extendsListLength, implementsTypes.length); + for (int i = 0; i < result.length; i++) { + PsiClassType type = result[i]; + result[i] = (PsiClassType)PsiUtil.captureToplevelWildcards(type, psiClass); + } + return result; + } + + @NotNull + private static PsiClassType getAnnotationSuperType(@NotNull PsiClass psiClass, @NotNull PsiElementFactory factory) { + return factory.createTypeByFQClassName("java.lang.annotation.Annotation", psiClass.getResolveScope()); + } + + private static PsiClassType getEnumSuperType(@NotNull PsiClass psiClass, @NotNull PsiElementFactory factory) { + PsiClassType superType; + final PsiManager manager = psiClass.getManager(); + final PsiClass enumClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Enum", psiClass.getResolveScope()); + if (enumClass == null) { + try { + superType = (PsiClassType)factory.createTypeFromText("java.lang.Enum", null); + } + catch (IncorrectOperationException e) { + superType = null; + } + } + else { + final PsiTypeParameter[] typeParameters = enumClass.getTypeParameters(); + PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; + if (typeParameters.length == 1) { + substitutor = substitutor.put(typeParameters[0], factory.createType(psiClass)); + } + superType = new PsiImmediateClassType(enumClass, substitutor); + } + return superType; + } + + @NotNull + public static PsiClass[] getInterfaces(@NotNull PsiTypeParameter typeParameter) { + final PsiClassType[] referencedTypes = typeParameter.getExtendsListTypes(); + if (referencedTypes.length == 0) { + return PsiClass.EMPTY_ARRAY; + } + final List<PsiClass> result = new ArrayList<PsiClass>(referencedTypes.length); + for (PsiClassType referencedType : referencedTypes) { + final PsiClass psiClass = referencedType.resolve(); + if (psiClass != null && psiClass.isInterface()) { + result.add(psiClass); + } + } + return result.toArray(new PsiClass[result.size()]); + } + + @NotNull + public static PsiClass[] getInterfaces(@NotNull PsiClass psiClass) { + if (psiClass.isInterface()) { + final PsiClassType[] extendsListTypes = psiClass.getExtendsListTypes(); + return resolveClassReferenceList(extendsListTypes, psiClass.getManager(), psiClass.getResolveScope(), false); + } + + if (psiClass instanceof PsiAnonymousClass) { + PsiClassType baseClassReference = ((PsiAnonymousClass)psiClass).getBaseClassType(); + PsiClass baseClass = baseClassReference.resolve(); + return baseClass != null && baseClass.isInterface() ? new PsiClass[]{baseClass} : PsiClass.EMPTY_ARRAY; + } + + final PsiClassType[] implementsListTypes = psiClass.getImplementsListTypes(); + return resolveClassReferenceList(implementsListTypes, psiClass.getManager(), psiClass.getResolveScope(), false); + } + + @NotNull + private static PsiClass[] resolveClassReferenceList(@NotNull PsiClassType[] listOfTypes, + @NotNull PsiManager manager, + @NotNull GlobalSearchScope resolveScope, + boolean includeObject) { + PsiClass objectClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope); + if (objectClass == null) includeObject = false; + if (listOfTypes.length == 0) { + if (includeObject) return new PsiClass[]{objectClass}; + return PsiClass.EMPTY_ARRAY; + } + + int referenceCount = listOfTypes.length; + if (includeObject) referenceCount++; + + PsiClass[] resolved = new PsiClass[referenceCount]; + int resolvedCount = 0; + + if (includeObject) resolved[resolvedCount++] = objectClass; + for (PsiClassType reference : listOfTypes) { + PsiClass refResolved = reference.resolve(); + if (refResolved != null) resolved[resolvedCount++] = refResolved; + } + + if (resolvedCount < referenceCount) { + PsiClass[] shorter = new PsiClass[resolvedCount]; + System.arraycopy(resolved, 0, shorter, 0, resolvedCount); + resolved = shorter; + } + + return resolved; + } + + @NotNull + public static List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(@NotNull PsiClass psiClass, + String name, + boolean checkBases) { + if (!checkBases) { + final PsiMethod[] methodsByName = psiClass.findMethodsByName(name, false); + final List<Pair<PsiMethod, PsiSubstitutor>> ret = new ArrayList<Pair<PsiMethod, PsiSubstitutor>>(methodsByName.length); + for (final PsiMethod method : methodsByName) { + ret.add(new Pair<PsiMethod, PsiSubstitutor>(method, PsiSubstitutor.EMPTY)); + } + return ret; + } + Map<String, List<Pair<PsiMember, PsiSubstitutor>>> map = getMap(psiClass, MemberType.METHOD); + @SuppressWarnings("unchecked") + List<Pair<PsiMethod, PsiSubstitutor>> list = (List)map.get(name); + return list == null ? + Collections.<Pair<PsiMethod, PsiSubstitutor>>emptyList() : + Collections.unmodifiableList(list); + } + + @NotNull + public static PsiClassType[] getExtendsListTypes(@NotNull PsiClass psiClass) { + if (psiClass.isEnum()) { + PsiClassType enumSuperType = getEnumSuperType(psiClass, JavaPsiFacade.getInstance(psiClass.getProject()).getElementFactory()); + return enumSuperType == null ? PsiClassType.EMPTY_ARRAY : new PsiClassType[]{enumSuperType}; + } + if (psiClass.isAnnotationType()) { + return new PsiClassType[]{getAnnotationSuperType(psiClass, JavaPsiFacade.getInstance(psiClass.getProject()).getElementFactory())}; + } + final PsiReferenceList extendsList = psiClass.getExtendsList(); + if (extendsList != null) { + return extendsList.getReferencedTypes(); + } + return PsiClassType.EMPTY_ARRAY; + } + + @NotNull + public static PsiClassType[] getImplementsListTypes(@NotNull PsiClass psiClass) { + final PsiReferenceList extendsList = psiClass.getImplementsList(); + if (extendsList != null) { + return extendsList.getReferencedTypes(); + } + return PsiClassType.EMPTY_ARRAY; + } + + public static boolean isClassEquivalentTo(@NotNull PsiClass aClass, PsiElement another) { + if (aClass == another) return true; + if (!(another instanceof PsiClass)) return false; + String name1 = aClass.getName(); + if (name1 == null) return false; + if (!another.isValid()) return false; + String name2 = ((PsiClass)another).getName(); + if (name2 == null) return false; + if (name1.hashCode() != name2.hashCode()) return false; + if (!name1.equals(name2)) return false; + String qName1 = aClass.getQualifiedName(); + String qName2 = ((PsiClass)another).getQualifiedName(); + if (qName1 == null || qName2 == null) { + //noinspection StringEquality + if (qName1 != qName2) return false; + + if (aClass instanceof PsiTypeParameter && another instanceof PsiTypeParameter) { + PsiTypeParameter p1 = (PsiTypeParameter)aClass; + PsiTypeParameter p2 = (PsiTypeParameter)another; + + return p1.getIndex() == p2.getIndex() && + aClass.getManager().areElementsEquivalent(p1.getOwner(), p2.getOwner()); + } + else { + return false; + } + } + if (qName1.hashCode() != qName2.hashCode() || !qName1.equals(qName2)) { + return false; + } + + if (originalElement(aClass).equals(originalElement((PsiClass)another))) { + return true; + } + + final PsiFile file1 = aClass.getContainingFile().getOriginalFile(); + final PsiFile file2 = another.getContainingFile().getOriginalFile(); + + //see com.intellij.openapi.vcs.changes.PsiChangeTracker + //see com.intellij.psi.impl.PsiFileFactoryImpl#createFileFromText(CharSequence,PsiFile) + final PsiFile original1 = file1.getUserData(PsiFileFactory.ORIGINAL_FILE); + final PsiFile original2 = file2.getUserData(PsiFileFactory.ORIGINAL_FILE); + if (original1 == original2 && original1 != null || original1 == file2 || original2 == file1 || file1 == file2) { + return compareClassSeqNumber(aClass, (PsiClass)another); + } + + final FileIndexFacade fileIndex = ServiceManager.getService(file1.getProject(), FileIndexFacade.class); + final VirtualFile vfile1 = file1.getViewProvider().getVirtualFile(); + final VirtualFile vfile2 = file2.getViewProvider().getVirtualFile(); + boolean lib1 = fileIndex.isInLibraryClasses(vfile1); + boolean lib2 = fileIndex.isInLibraryClasses(vfile2); + + return (fileIndex.isInSource(vfile1) || lib1) && (fileIndex.isInSource(vfile2) || lib2); + } + + private static boolean compareClassSeqNumber(@NotNull PsiClass aClass, @NotNull PsiClass another) { + // there may be several classes in one file, they must not be equal + int index1 = getSeqNumber(aClass); + if (index1 == -1) return true; + int index2 = getSeqNumber(another); + return index1 == index2; + } + + private static int getSeqNumber(@NotNull PsiClass aClass) { + // sequence number of this class among its parent' child classes named the same + PsiElement parent = aClass.getParent(); + if (parent == null) return -1; + int seqNo = 0; + for (PsiElement child : parent.getChildren()) { + if (child == aClass) return seqNo; + if (child instanceof PsiClass && Comparing.strEqual(aClass.getName(), ((PsiClass)child).getName())) { + seqNo++; + } + } + return -1; + } + + @NotNull + private static PsiElement originalElement(@NotNull PsiClass aClass) { + final PsiElement originalElement = aClass.getOriginalElement(); + ASTNode node = originalElement.getNode(); + if (node != null) { + final PsiCompiledElement compiled = node.getUserData(ClsElementImpl.COMPILED_ELEMENT); + if (compiled != null) { + return compiled; + } + } + return originalElement; + } + + public static boolean isFieldEquivalentTo(@NotNull PsiField field, PsiElement another) { + if (!(another instanceof PsiField)) return false; + String name1 = field.getName(); + if (name1 == null) return false; + if (!another.isValid()) return false; + + String name2 = ((PsiField)another).getName(); + if (!name1.equals(name2)) return false; + PsiClass aClass1 = field.getContainingClass(); + PsiClass aClass2 = ((PsiField)another).getContainingClass(); + return aClass1 != null && aClass2 != null && field.getManager().areElementsEquivalent(aClass1, aClass2); + } + + public static boolean isMethodEquivalentTo(@NotNull PsiMethod method1, PsiElement another) { + if (method1 == another) return true; + if (!(another instanceof PsiMethod)) return false; + PsiMethod method2 = (PsiMethod)another; + if (!another.isValid()) return false; + if (!method1.getName().equals(method2.getName())) return false; + PsiClass aClass1 = method1.getContainingClass(); + PsiClass aClass2 = method2.getContainingClass(); + PsiManager manager = method1.getManager(); + if (!(aClass1 != null && aClass2 != null && manager.areElementsEquivalent(aClass1, aClass2))) return false; + + PsiParameter[] parameters1 = method1.getParameterList().getParameters(); + PsiParameter[] parameters2 = method2.getParameterList().getParameters(); + if (parameters1.length != parameters2.length) return false; + for (int i = 0; i < parameters1.length; i++) { + PsiParameter parameter1 = parameters1[i]; + PsiParameter parameter2 = parameters2[i]; + PsiType type1 = parameter1.getType(); + PsiType type2 = parameter2.getType(); + if (!compareParamTypes(manager, type1, type2, new HashSet<String>())) return false; + } + return true; + } + + private static boolean compareParamTypes(@NotNull PsiManager manager, @NotNull PsiType type1, @NotNull PsiType type2, Set<String> visited) { + if (type1 instanceof PsiArrayType) { + if (type2 instanceof PsiArrayType) { + final PsiType componentType1 = ((PsiArrayType)type1).getComponentType(); + final PsiType componentType2 = ((PsiArrayType)type2).getComponentType(); + if (compareParamTypes(manager, componentType1, componentType2, visited)) return true; + } + return false; + } + + if (!(type1 instanceof PsiClassType) || !(type2 instanceof PsiClassType)) { + return type1.equals(type2); + } + + PsiClass class1 = ((PsiClassType)type1).resolve(); + PsiClass class2 = ((PsiClassType)type2).resolve(); + visited.add(type1.getCanonicalText()); + visited.add(type2.getCanonicalText()); + + if (class1 instanceof PsiTypeParameter && class2 instanceof PsiTypeParameter) { + if (!(Comparing.equal(class1.getName(), class2.getName()) && ((PsiTypeParameter)class1).getIndex() == ((PsiTypeParameter)class2).getIndex())) return false; + final PsiClassType[] eTypes1 = class1.getExtendsListTypes(); + final PsiClassType[] eTypes2 = class2.getExtendsListTypes(); + if (eTypes1.length != eTypes2.length) return false; + for (int i = 0; i < eTypes1.length; i++) { + PsiClassType eType1 = eTypes1[i]; + PsiClassType eType2 = eTypes2[i]; + if (visited.contains(eType1.getCanonicalText()) || visited.contains(eType2.getCanonicalText())) { + return false; + } + if (!compareParamTypes(manager, eType1, eType2, visited)) return false; + } + return true; + } + + return manager.areElementsEquivalent(class1, class2); + } +} |