deferred icons fix -- caches for different flags
[idea/community.git] / java / java-impl / src / com / intellij / psi / impl / PsiClassImplUtil.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.roots.ProjectFileIndex;
20 import com.intellij.openapi.roots.ProjectRootManager;
21 import com.intellij.openapi.util.*;
22 import com.intellij.openapi.vfs.VirtualFile;
23 import com.intellij.psi.*;
24 import com.intellij.psi.augment.PsiAugmentProvider;
25 import com.intellij.psi.filters.OrFilter;
26 import com.intellij.psi.impl.compiled.ClsElementImpl;
27 import com.intellij.psi.impl.source.PsiImmediateClassType;
28 import com.intellij.psi.infos.MethodCandidateInfo;
29 import com.intellij.psi.scope.ElementClassFilter;
30 import com.intellij.psi.scope.ElementClassHint;
31 import com.intellij.psi.scope.NameHint;
32 import com.intellij.psi.scope.PsiScopeProcessor;
33 import com.intellij.psi.scope.processor.FilterScopeProcessor;
34 import com.intellij.psi.scope.processor.MethodResolverProcessor;
35 import com.intellij.psi.search.GlobalSearchScope;
36 import com.intellij.psi.search.LocalSearchScope;
37 import com.intellij.psi.search.PackageScope;
38 import com.intellij.psi.search.SearchScope;
39 import com.intellij.psi.util.*;
40 import com.intellij.ui.IconDeferrer;
41 import com.intellij.ui.RowIcon;
42 import com.intellij.util.Function;
43 import com.intellij.util.IncorrectOperationException;
44 import com.intellij.util.ReflectionCache;
45 import com.intellij.util.SmartList;
46 import com.intellij.util.containers.HashMap;
47 import gnu.trove.THashSet;
48 import org.jetbrains.annotations.NonNls;
49 import org.jetbrains.annotations.NotNull;
50 import org.jetbrains.annotations.Nullable;
51
52 import javax.swing.*;
53 import java.util.*;
54
55 /**
56  * Created by IntelliJ IDEA.
57  * User: ik
58  * Date: 24.10.2003
59  * Time: 16:50:37
60  * To change this template use Options | File Templates.
61  */
62 public class PsiClassImplUtil {
63   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiClassImplUtil");
64   private static final Key<Boolean> NAME_MAPS_BUILT_FLAG = Key.create("NAME_MAPS_BUILT_FLAG");
65
66   private static final Key<CachedValue<Map>> MAP_IN_CLASS_KEY = Key.create("MAP_KEY");
67
68   @NotNull public static PsiField[] getAllFields(final PsiClass aClass) {
69     List<PsiField> map = getAllByMap(aClass, PsiField.class);
70     return map.toArray(new PsiField[map.size()]);
71   }
72
73   @NotNull public static PsiMethod[] getAllMethods(final PsiClass aClass) {
74     List<PsiMethod> methods = getAllByMap(aClass, PsiMethod.class);
75     return methods.toArray(new PsiMethod[methods.size()]);
76   }
77
78   @NotNull public static PsiClass[] getAllInnerClasses(PsiClass aClass) {
79     List<PsiClass> classes = getAllByMap(aClass, PsiClass.class);
80     return classes.toArray(new PsiClass[classes.size()]);
81   }
82
83   @Nullable public static PsiField findFieldByName(PsiClass aClass, String name, boolean checkBases) {
84     final List<PsiField> byMap = findByMap(aClass, name, checkBases, PsiField.class);
85     return byMap.isEmpty() ? null : byMap.get(0);
86   }
87
88   @NotNull public static PsiMethod[] findMethodsByName(PsiClass aClass, String name, boolean checkBases) {
89     List<PsiMethod> methods = findByMap(aClass, name, checkBases, PsiMethod.class);
90     return methods.toArray(new PsiMethod[methods.size()]);
91   }
92
93   @Nullable public static PsiMethod findMethodBySignature(final PsiClass aClass, final PsiMethod patternMethod, final boolean checkBases) {
94     final List<PsiMethod> result = findMethodsBySignature(aClass, patternMethod, checkBases, true);
95     return result.isEmpty() ? null : result.get(0);
96   }
97
98   // ----------------------------- findMethodsBySignature -----------------------------------
99
100   @NotNull public static PsiMethod[] findMethodsBySignature(final PsiClass aClass, final PsiMethod patternMethod, final boolean checkBases) {
101     List<PsiMethod> methods = findMethodsBySignature(aClass, patternMethod, checkBases, false);
102     return methods.toArray(new PsiMethod[methods.size()]);
103   }
104
105   @NotNull private static List<PsiMethod> findMethodsBySignature(final PsiClass aClass,
106                                                     final PsiMethod patternMethod,
107                                                     final boolean checkBases,
108                                                     final boolean stopOnFirst) {
109 /*    final MethodSignature patternSignature = MethodSignatureBackedByPsiMethod.create(patternMethod, PsiSubstitutor.EMPTY);
110     if (!checkBases) {
111       final PsiMethod[] methodsByName = aClass.findMethodsByName(patternMethod.getName(), false);
112       if (methodsByName.length == 0) return PsiMethod.EMPTY_ARRAY;
113       List<PsiMethod> result = new ArrayList<PsiMethod>();
114       for (PsiMethod method : methodsByName) {
115         final MethodSignature otherSignature = method.getSignature(PsiSubstitutor.EMPTY);
116         if (otherSignature.equals(patternSignature)) {
117           result.add(method);
118           if (stopOnFirst) break;
119         }
120       }
121
122       return result.toArray(new PsiMethod[result.size()]);
123     }
124     else {
125       final Set<HierarchicalMethodSignature> signatures = getOverrideEquivalentSignatures(aClass);
126       final HierarchicalMethodSignature signatureWithSupers = signatures.get(patternSignature);
127       if (signatureWithSupers == null) return PsiMethod.EMPTY_ARRAY;
128       final List<PsiMethod> result = new ArrayList<PsiMethod>();
129       MethodSignatureUtil.processMethodHierarchy(signatureWithSupers, new Processor<HierarchicalMethodSignature>() {
130         public boolean process(final HierarchicalMethodSignature sig) {
131           result.add(sig.getSignature().getMethod());
132           return !stopOnFirst;
133         }
134       });
135       return result.toArray(new PsiMethod[result.size()]);
136     }*/
137
138     final PsiMethod[] methodsByName = aClass.findMethodsByName(patternMethod.getName(), checkBases);
139     if (methodsByName.length == 0) return Collections.emptyList();
140     final List<PsiMethod> methods = new SmartList<PsiMethod>();
141     final MethodSignature patternSignature = patternMethod.getSignature(PsiSubstitutor.EMPTY);
142     for (final PsiMethod method : methodsByName) {
143       final PsiClass superClass = method.getContainingClass();
144       final PsiSubstitutor substitutor;
145       if (checkBases && !aClass.equals(superClass)) {
146         substitutor = TypeConversionUtil.getSuperClassSubstitutor(superClass, aClass, PsiSubstitutor.EMPTY);
147       }
148       else {
149         substitutor = PsiSubstitutor.EMPTY;
150       }
151       final MethodSignature signature = method.getSignature(substitutor);
152       if (signature.equals(patternSignature)) {
153         methods.add(method);
154         if (stopOnFirst) {
155           break;
156         }
157       }
158     }
159     return methods;
160   }
161
162   // ----------------------------------------------------------------------------------------
163
164   @Nullable public static PsiClass findInnerByName(PsiClass aClass, String name, boolean checkBases) {
165     final List<PsiClass> byMap = findByMap(aClass, name, checkBases, PsiClass.class);
166     return byMap.isEmpty() ? null : byMap.get(0);
167   }
168
169   @SuppressWarnings({"unchecked"})
170   @NotNull private static <T extends PsiMember> List<T> findByMap(PsiClass aClass, String name, boolean checkBases, Class<T> type) {
171     if (name == null) return Collections.emptyList();
172
173     if (!checkBases) {
174       T[] members = null;
175       if (ReflectionCache.isAssignable(type,PsiMethod.class)) {
176         members = (T[])aClass.getMethods();
177       }
178       else if (ReflectionCache.isAssignable(type,PsiClass.class)) {
179         members = (T[])aClass.getInnerClasses();
180       }
181       else if (ReflectionCache.isAssignable(type,PsiField.class)) {
182         members = (T[])aClass.getFields();
183       }
184       if (members == null) return Collections.emptyList();
185
186       List<T> list = new ArrayList<T>();
187       for (T member : members) {
188         if (name.equals(member.getName())) list.add(member);
189       }
190       return list;
191     }
192     else {
193       final Map<String, List<Pair<T, PsiSubstitutor>>> allMethodsMap = getMap(aClass, type);
194       final List<Pair<T, PsiSubstitutor>> list = allMethodsMap.get(name);
195       if (list == null) return Collections.emptyList();
196       final List<T> ret = new ArrayList<T>();
197       for (final Pair<T, PsiSubstitutor> info : list) {
198         ret.add(info.getFirst());
199       }
200
201       return ret;
202     }
203   }
204
205   public static <T extends PsiMember> List<Pair<T, PsiSubstitutor>> getAllWithSubstitutorsByMap(PsiClass aClass, Class<T> type) {
206     final Map<String, List<Pair<T, PsiSubstitutor>>> allMap = getMap(aClass, type);
207     return allMap.get(ALL);
208   }
209
210   @NotNull private static <T extends PsiMember> List<T> getAllByMap(PsiClass aClass, Class<T> type) {
211     List<Pair<T, PsiSubstitutor>> pairs = getAllWithSubstitutorsByMap(aClass, type);
212
213     assert pairs != null : "pairs should be already computed. Wrong allMap: " + getMap(aClass, type);
214
215     final List<T> ret = new ArrayList<T>(pairs.size());
216     for (final Pair<T, PsiSubstitutor> pair : pairs) {
217       T t = pair.getFirst();
218       LOG.assertTrue(t != null, aClass);
219       ret.add(t);
220     }
221     return ret;
222   }
223
224   @NonNls private static final String ALL = "Intellij-IDEA-ALL";
225
226   private static Map<Class<? extends PsiMember>, Map<String, List<Pair<PsiMember, PsiSubstitutor>>>> buildAllMaps(final PsiClass psiClass) {
227     final List<Pair<PsiMember, PsiSubstitutor>> classes = new ArrayList<Pair<PsiMember, PsiSubstitutor>>();
228     final List<Pair<PsiMember, PsiSubstitutor>> fields = new ArrayList<Pair<PsiMember, PsiSubstitutor>>();
229     final List<Pair<PsiMember, PsiSubstitutor>> methods = new ArrayList<Pair<PsiMember, PsiSubstitutor>>();
230
231     FilterScopeProcessor<MethodCandidateInfo> processor = new FilterScopeProcessor<MethodCandidateInfo>(
232       new OrFilter(ElementClassFilter.METHOD, ElementClassFilter.FIELD, ElementClassFilter.CLASS)) {
233       protected void add(PsiElement element, PsiSubstitutor substitutor) {
234         if (element instanceof PsiMethod) {
235           methods.add(new Pair<PsiMember, PsiSubstitutor>((PsiMethod)element, substitutor));
236         }
237         else if (element instanceof PsiField) {
238           fields.add(new Pair<PsiMember, PsiSubstitutor>((PsiField)element, substitutor));
239         }
240         else if (element instanceof PsiClass) {
241           classes.add(new Pair<PsiMember, PsiSubstitutor>((PsiClass)element, substitutor));
242         }
243       }
244     };
245     PsiElementFactory factory = JavaPsiFacade.getInstance(psiClass.getProject()).getElementFactory();
246     processDeclarationsInClassNotCached(psiClass, processor, ResolveState.initial(),  new THashSet<PsiClass>(), null, psiClass, false, factory);
247
248     Map<Class<? extends PsiMember>, Map<String, List<Pair<PsiMember, PsiSubstitutor>>>> result = new HashMap<Class<? extends PsiMember>, Map<String, List<Pair<PsiMember, PsiSubstitutor>>>>(3);
249     result.put(PsiClass.class, generateMapByList(classes));
250     result.put(PsiMethod.class, generateMapByList(methods));
251     result.put(PsiField.class, generateMapByList(fields));
252     psiClass.putUserData(NAME_MAPS_BUILT_FLAG, Boolean.TRUE);
253     return result;
254   }
255
256   private static Map<String, List<Pair<PsiMember, PsiSubstitutor>>> generateMapByList(@NotNull final List<Pair<PsiMember, PsiSubstitutor>> list) {
257     Map<String, List<Pair<PsiMember, PsiSubstitutor>>> map = new HashMap<String, List<Pair<PsiMember, PsiSubstitutor>>>();
258     map.put(ALL, list);
259     for (final Pair<PsiMember, PsiSubstitutor> info : list) {
260       final PsiMember element = info.getFirst();
261       final String currentName = element.getName();
262       List<Pair<PsiMember, PsiSubstitutor>> listByName = map.get(currentName);
263       if (listByName == null) {
264         listByName = new ArrayList<Pair<PsiMember, PsiSubstitutor>>(1);
265         map.put(currentName, listByName);
266       }
267       listByName.add(info);
268     }
269     return map;
270   }
271
272   private static <T extends PsiMember> Map<String, List<Pair<T, PsiSubstitutor>>> getMap(final PsiClass aClass, Class<T> memberClazz) {
273     CachedValue<Map> value = aClass.getUserData(MAP_IN_CLASS_KEY);
274     if (value == null) {
275       final CachedValueProvider<Map> provider = new ByNameCachedValueProvider(aClass);
276       value = CachedValuesManager.getManager(aClass.getProject()).createCachedValue(provider, false);
277       //Do not cache for nonphysical elements
278       if (aClass.isPhysical()) {
279         value = ((UserDataHolderEx)aClass).putUserDataIfAbsent(MAP_IN_CLASS_KEY, value);
280       }
281     }
282     return (Map<String, List<Pair<T, PsiSubstitutor>>>)value.getValue().get(memberClazz);
283   }
284
285   private static class ClassIconRequest {
286     public PsiClass psiClass;
287     public int flags;
288
289     private ClassIconRequest(PsiClass psiClass, int flags) {
290       this.psiClass = psiClass;
291       this.flags = flags;
292     }
293
294     @Override
295     public boolean equals(Object o) {
296       if (this == o) return true;
297       if (!(o instanceof ClassIconRequest)) return false;
298
299       ClassIconRequest that = (ClassIconRequest)o;
300
301       if (flags != that.flags) return false;
302       if (psiClass != null ? !psiClass.equals(that.psiClass) : that.psiClass != null) return false;
303
304       return true;
305     }
306
307     @Override
308     public int hashCode() {
309       int result = psiClass != null ? psiClass.hashCode() : 0;
310       result = 31 * result + flags;
311       return result;
312     }
313   }
314
315   private static final Function<ClassIconRequest, Icon> FULL_ICON_EVALUATOR = new Function<ClassIconRequest, Icon>() {
316     public Icon fun(ClassIconRequest r) {
317       if (!r.psiClass.isValid() || r.psiClass.getProject().isDisposed()) return null;
318
319       final boolean isLocked = (r.flags & Iconable.ICON_FLAG_READ_STATUS) != 0 && !r.psiClass.isWritable();
320       Icon symbolIcon = ElementPresentationUtil.getClassIconOfKind(r.psiClass, ElementPresentationUtil.getClassKind(r.psiClass));
321       RowIcon baseIcon = ElementBase.createLayeredIcon(symbolIcon, ElementPresentationUtil.getFlags(r.psiClass, isLocked));
322       return ElementPresentationUtil.addVisibilityIcon(r.psiClass, r.flags, baseIcon);
323     }
324   };
325
326   public static Icon getClassIcon(final int flags, final PsiClass aClass) {
327     Icon base = Iconable.LastComputedIcon.get(aClass, flags);
328     if (base == null) {
329       Icon symbolIcon = ElementPresentationUtil.getClassIconOfKind(aClass, ElementPresentationUtil.getBasicClassKind(aClass));
330       RowIcon baseIcon = ElementBase.createLayeredIcon(symbolIcon, 0);
331       base = ElementPresentationUtil.addVisibilityIcon(aClass, flags, baseIcon);
332     }
333
334     return IconDeferrer.getInstance().defer(base, new ClassIconRequest(aClass, flags), FULL_ICON_EVALUATOR);
335   }
336
337   public static SearchScope getClassUseScope(final PsiClass aClass) {
338     final GlobalSearchScope maximalUseScope = ((PsiManagerEx) aClass.getManager()).getFileManager().getUseScope(aClass);
339     if (aClass instanceof PsiAnonymousClass) {
340       return new LocalSearchScope(aClass);
341     }
342     PsiFile file = aClass.getContainingFile();
343     if (JspPsiUtil.isInJspFile(file)) return maximalUseScope;
344     final PsiClass containingClass = aClass.getContainingClass();
345     if (aClass.hasModifierProperty(PsiModifier.PUBLIC)) {
346       return containingClass != null ? containingClass.getUseScope() : maximalUseScope;
347     }
348     else if (aClass.hasModifierProperty(PsiModifier.PROTECTED)) {
349       return containingClass != null ? containingClass.getUseScope() : maximalUseScope;
350     }
351     else if (aClass.hasModifierProperty(PsiModifier.PRIVATE) || aClass instanceof PsiTypeParameter) {
352       PsiClass topClass = PsiUtil.getTopLevelClass(aClass);
353       return new LocalSearchScope(topClass == null ? aClass.getContainingFile() : topClass);
354     }
355     else {
356       PsiPackage aPackage = null;
357       if (file instanceof PsiJavaFile) {
358         aPackage = JavaPsiFacade.getInstance(aClass.getProject()).findPackage(((PsiJavaFile)file).getPackageName());
359       }
360
361       if (aPackage == null) {
362         PsiDirectory dir = file.getContainingDirectory();
363         if (dir != null) {
364           aPackage = JavaDirectoryService.getInstance().getPackage(dir);
365         }
366       }
367
368       if (aPackage != null) {
369         SearchScope scope = PackageScope.packageScope(aPackage, false);
370         scope = scope.intersectWith(maximalUseScope);
371         return scope;
372       }
373
374       return new LocalSearchScope(file);
375     }
376   }
377
378   public static boolean isMainMethod(PsiMethod method) {
379     if (!PsiType.VOID.equals(method.getReturnType())) return false;
380     PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
381     try {
382       PsiMethod appMain = factory.createMethodFromText("void main(String[] args);", null);
383       if (MethodSignatureUtil.areSignaturesEqual(method, appMain)) return true;
384       PsiMethod appPremain = factory.createMethodFromText("void premain(String args, java.lang.instrument.Instrumentation i);", null);
385       if (MethodSignatureUtil.areSignaturesEqual(method, appPremain)) return true;
386     }
387     catch (IncorrectOperationException e) {
388       LOG.error(e);
389     }
390     return false;
391   }
392
393   private static class ByNameCachedValueProvider implements CachedValueProvider<Map> {
394     private final PsiClass myClass;
395
396     private ByNameCachedValueProvider(final PsiClass aClass) {
397       myClass = aClass;
398     }
399
400     public Result<Map> compute() {
401       final Map<Class<? extends PsiMember>, Map<String, List<Pair<PsiMember, PsiSubstitutor>>>> map = buildAllMaps(myClass);
402       return new Result<Map>(map, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
403     }
404   }
405
406   public static boolean processDeclarationsInClass(PsiClass aClass,
407                                                    PsiScopeProcessor processor,
408                                                    ResolveState state,
409                                                    Set<PsiClass> visited,
410                                                    PsiElement last,
411                                                    PsiElement place,
412                                                    boolean isRaw) {
413     if (visited != null && visited.contains(aClass)) return true;
414     PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
415     isRaw = isRaw || PsiUtil.isRawSubstitutor(aClass, substitutor);
416     if (last instanceof PsiTypeParameterList || last instanceof PsiModifierList) return true; //TypeParameterList and ModifierList do not see our declarations
417     final Boolean built = aClass.getUserData(NAME_MAPS_BUILT_FLAG);
418     PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory();
419     if (built == null) {
420       return processDeclarationsInClassNotCached(aClass, processor, state, visited, last, place, isRaw, factory);
421     }
422
423     final NameHint nameHint = processor.getHint(NameHint.KEY);
424     final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
425
426     if (nameHint != null) {
427       if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.FIELD)) {
428         final PsiField fieldByName = aClass.findFieldByName(nameHint.getName(state), false);
429         if (fieldByName != null) {
430           processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
431           if (!processor.execute(fieldByName, state)) return false;
432         }
433         else {
434           final Map<String, List<Pair<PsiField, PsiSubstitutor>>> allFieldsMap = getMap(aClass, PsiField.class);
435
436           final List<Pair<PsiField, PsiSubstitutor>> list = allFieldsMap.get(nameHint.getName(state));
437           if (list != null) {
438             for (final Pair<PsiField, PsiSubstitutor> candidate : list) {
439               PsiField candidateField = candidate.getFirst();
440               PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(candidateField.getContainingClass(), candidate.getSecond(), aClass,
441                                                                        substitutor, place, factory);
442
443               processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, candidateField.getContainingClass());
444               if (!processor.execute(candidateField, state.put(PsiSubstitutor.KEY, finalSubstitutor))) return false;
445             }
446           }
447         }
448       }
449       if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.CLASS)) {
450         if (last != null && last.getParent() == aClass) {
451           if (last instanceof PsiClass) {
452             if (!processor.execute(last, state)) return false;
453           }
454           // Parameters
455           final PsiTypeParameterList list = aClass.getTypeParameterList();
456           if (list != null && !list.processDeclarations(processor, state, last, place)) return false;
457         }
458         if (!(last instanceof PsiReferenceList)) {
459           final PsiClass classByName = aClass.findInnerClassByName(nameHint.getName(state), false);
460           if (classByName != null) {
461             processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
462             if (!processor.execute(classByName, state)) return false;
463           }
464           else {
465             final Map<String, List<Pair<PsiClass, PsiSubstitutor>>> allClassesMap = getMap(aClass, PsiClass.class);
466
467             final List<Pair<PsiClass, PsiSubstitutor>> list = allClassesMap.get(nameHint.getName(state));
468             if (list != null) {
469               for (final Pair<PsiClass, PsiSubstitutor> candidate : list) {
470                 final PsiClass inner = candidate.getFirst();
471                 final PsiClass containingClass = inner.getContainingClass();
472                 if (containingClass != null) {
473                   PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(containingClass, candidate.getSecond(), aClass,
474                                                                            substitutor, place, factory);
475                   processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass);
476                   if (!processor.execute(inner, state.put(PsiSubstitutor.KEY, finalSubstitutor))) return false;
477                 }
478               }
479             }
480           }
481         }
482       }
483       if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.METHOD)) {
484         if (processor instanceof MethodResolverProcessor) {
485           final MethodResolverProcessor methodResolverProcessor = (MethodResolverProcessor)processor;
486           if (methodResolverProcessor.isConstructor()) {
487             final PsiMethod[] constructors = aClass.getConstructors();
488             methodResolverProcessor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
489             for (PsiMethod constructor : constructors) {
490               if (!methodResolverProcessor.execute(constructor, state)) return false;
491             }
492             return true;
493           }
494         }
495         final Map<String, List<Pair<PsiMethod, PsiSubstitutor>>> allMethodsMap = getMap(aClass, PsiMethod.class);
496         final List<Pair<PsiMethod, PsiSubstitutor>> list = allMethodsMap.get(nameHint.getName(state));
497         if (list != null) {
498           for (final Pair<PsiMethod, PsiSubstitutor> candidate : list) {
499             PsiMethod candidateMethod = candidate.getFirst();
500             if (processor instanceof MethodResolverProcessor) {
501               if (candidateMethod.isConstructor() != ((MethodResolverProcessor)processor).isConstructor()) continue;
502             }
503             final PsiClass containingClass = candidateMethod.getContainingClass();
504             PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(containingClass, candidate.getSecond(), aClass,
505                                                                      substitutor, place, factory);
506             if (isRaw && !candidateMethod.hasModifierProperty(PsiModifier.STATIC)) { //static methods are not erased due to raw overriding
507               PsiTypeParameter[] methodTypeParameters = candidateMethod.getTypeParameters();
508               finalSubstitutor = factory.createRawSubstitutor(finalSubstitutor, methodTypeParameters);
509             }
510             processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass);
511             if (!processor.execute(candidateMethod, state.put(PsiSubstitutor.KEY, finalSubstitutor))) return false;
512           }
513         }
514       }
515       return true;
516     }
517
518     return processDeclarationsInClassNotCached(aClass, processor, state, visited, last, place, isRaw, factory);
519   }
520
521   public static PsiSubstitutor obtainFinalSubstitutor(@NotNull PsiClass candidateClass, PsiSubstitutor candidateSubstitutor, PsiClass aClass,
522                                                        PsiSubstitutor substitutor,
523                                                        final PsiElement place,
524                                                        PsiElementFactory elementFactory) {
525     if (PsiUtil.isRawSubstitutor(aClass, substitutor)) {
526       return elementFactory.createRawSubstitutor(candidateClass);
527     }
528
529     final PsiType containingType = elementFactory.createType(candidateClass, candidateSubstitutor, PsiUtil.getLanguageLevel(place));
530     PsiType type = substitutor.substitute(containingType);
531     if (!(type instanceof PsiClassType)) return candidateSubstitutor;
532     return ((PsiClassType)type).resolveGenerics().getSubstitutor();
533   }
534
535   private static boolean processDeclarationsInClassNotCached(PsiClass aClass, PsiScopeProcessor processor, ResolveState state, Set<PsiClass> visited,
536                                                              PsiElement last,
537                                                              PsiElement place,
538                                                              boolean isRaw,
539                                                              PsiElementFactory factory) {
540     if (visited == null) visited = new THashSet<PsiClass>();
541     if (!visited.add(aClass)) return true;
542     processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
543     final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
544     final NameHint nameHint = processor.getHint(NameHint.KEY);
545
546
547     if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.FIELD)) {
548       if (nameHint != null) {
549         final PsiField fieldByName = aClass.findFieldByName(nameHint.getName(state), false);
550         if (fieldByName != null) {
551           if (!processor.execute(fieldByName, state)) return false;
552         }
553       }
554       else {
555         final PsiField[] fields = aClass.getFields();
556         for (final PsiField field : fields) {
557           if (!processor.execute(field, state)) return false;
558         }
559       }
560
561       for (PsiField field : PsiAugmentProvider.collectAugments(aClass, PsiField.class)) {
562         if (!processor.execute(field, state)) return false;
563       }
564     }
565
566     if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.METHOD)) {
567       final PsiMethod[] methods = nameHint != null ? aClass.findMethodsByName(nameHint.getName(state), false) : aClass.getMethods();
568       for (final PsiMethod method : methods) {
569         if (isRaw && !method.hasModifierProperty(PsiModifier.STATIC)) { //static methods are not erased due to raw overriding
570           PsiTypeParameter[] methodTypeParameters = method.getTypeParameters();
571           PsiSubstitutor raw = factory.createRawSubstitutor(state.get(PsiSubstitutor.KEY), methodTypeParameters);
572           state = state.put(PsiSubstitutor.KEY, raw);
573         }
574         if (!processor.execute(method, state)) return false;
575       }
576
577       for (PsiMethod method : PsiAugmentProvider.collectAugments(aClass, PsiMethod.class)) {
578         if (!processor.execute(method, state)) return false;
579       }
580     }
581
582     if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.CLASS)) {
583       if (last != null && last.getParent() == aClass) {
584         // Parameters
585         final PsiTypeParameterList list = aClass.getTypeParameterList();
586         if (list != null && !list.processDeclarations(processor, ResolveState.initial(), last, place)) return false;
587       }
588
589       if (!(last instanceof PsiReferenceList) && !(last instanceof PsiModifierList)) {
590         // Inners
591         if (nameHint != null) {
592           final PsiClass inner = aClass.findInnerClassByName(nameHint.getName(state), false);
593           if (inner != null) {
594             if (!processor.execute(inner, state)) return false;
595           }
596         }
597         else {
598           final PsiClass[] inners = aClass.getInnerClasses();
599           for (final PsiClass inner : inners) {
600             if (!processor.execute(inner, state)) return false;
601           }
602         }
603       }
604     }
605
606     return last instanceof PsiReferenceList || processSuperTypes(aClass, processor, visited, last, place, state, isRaw, factory);
607   }
608
609   private static boolean processSuperTypes(PsiClass aClass,
610                                            PsiScopeProcessor processor,
611                                            Set<PsiClass> visited,
612                                            PsiElement last,
613                                            PsiElement place,
614                                            ResolveState state,
615                                            boolean isRaw, PsiElementFactory factory) {
616     for (final PsiClassType superType : aClass.getSuperTypes()) {
617       final PsiClassType.ClassResolveResult superTypeResolveResult = superType.resolveGenerics();
618       PsiClass superClass = superTypeResolveResult.getElement();
619       if (superClass == null) continue;
620       PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(superClass, superTypeResolveResult.getSubstitutor(), aClass, state.get(PsiSubstitutor.KEY),
621                                                                place, factory);
622       if (!processDeclarationsInClass(superClass, processor, state.put(PsiSubstitutor.KEY, finalSubstitutor), visited, last, place, isRaw)) {
623         return false;
624       }
625     }
626     return true;
627   }
628
629   @Nullable
630   public static PsiClass getSuperClass(PsiClass psiClass) {
631     PsiManager manager = psiClass.getManager();
632     GlobalSearchScope resolveScope = psiClass.getResolveScope();
633
634     final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
635     if (psiClass.isInterface()) {
636       return facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope);
637     }
638     if (psiClass.isEnum()) {
639       return facade.findClass(CommonClassNames.JAVA_LANG_ENUM, resolveScope);
640     }
641
642     if (psiClass instanceof PsiAnonymousClass) {
643       PsiClassType baseClassReference = ((PsiAnonymousClass)psiClass).getBaseClassType();
644       PsiClass baseClass = baseClassReference.resolve();
645       if (baseClass == null || baseClass.isInterface()) return facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope);
646       return baseClass;
647     }
648
649     if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) return null;
650
651     final PsiClassType[] referenceElements = psiClass.getExtendsListTypes();
652
653     if (referenceElements.length == 0) return facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope);
654
655     PsiClass psiResoved = referenceElements[0].resolve();
656     return psiResoved == null ? facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope) : psiResoved;
657   }
658
659   @NotNull public static PsiClass[] getSupers(PsiClass psiClass) {
660     final PsiClass[] supers = getSupersInner(psiClass);
661     for (final PsiClass aSuper : supers) {
662       LOG.assertTrue(aSuper != null);
663     }
664     return supers;
665   }
666
667   private static PsiClass[] getSupersInner(PsiClass psiClass) {
668     PsiClassType[] extendsListTypes = psiClass.getExtendsListTypes();
669     PsiClassType[] implementsListTypes = psiClass.getImplementsListTypes();
670
671     if (psiClass.isInterface()) {
672       return resolveClassReferenceList(extendsListTypes,
673                                        psiClass.getManager(), psiClass.getResolveScope(), true);
674     }
675
676     if (psiClass instanceof PsiAnonymousClass) {
677       PsiAnonymousClass psiAnonymousClass = (PsiAnonymousClass)psiClass;
678       PsiClassType baseClassReference = psiAnonymousClass.getBaseClassType();
679       PsiClass baseClass = baseClassReference.resolve();
680       if (baseClass != null) {
681         if (baseClass.isInterface()) {
682           PsiClass objectClass = JavaPsiFacade.getInstance(psiClass.getProject()).findClass("java.lang.Object", psiClass.getResolveScope());
683           return objectClass != null ? new PsiClass[]{objectClass, baseClass} : new PsiClass[]{baseClass};
684         }
685         return new PsiClass[]{baseClass};
686       }
687
688       PsiClass objectClass = JavaPsiFacade.getInstance(psiClass.getProject()).findClass("java.lang.Object", psiClass.getResolveScope());
689       return objectClass != null ? new PsiClass[]{objectClass} : PsiClass.EMPTY_ARRAY;
690     }
691     else if (psiClass instanceof PsiTypeParameter) {
692       if (extendsListTypes.length == 0) {
693         final PsiClass objectClass =
694           JavaPsiFacade.getInstance(psiClass.getProject()).findClass("java.lang.Object", psiClass.getResolveScope());
695         return objectClass != null ? new PsiClass[]{objectClass} : PsiClass.EMPTY_ARRAY;
696       }
697       return resolveClassReferenceList(extendsListTypes, psiClass.getManager(),
698                                        psiClass.getResolveScope(), false);
699     }
700
701     PsiClass[] interfaces = resolveClassReferenceList(implementsListTypes, psiClass.getManager(), psiClass.getResolveScope(), false);
702
703     PsiClass superClass = getSuperClass(psiClass);
704     if (superClass == null) return interfaces;
705     PsiClass[] types = new PsiClass[interfaces.length + 1];
706     types[0] = superClass;
707     System.arraycopy(interfaces, 0, types, 1, interfaces.length);
708
709     return types;
710   }
711
712   @NotNull public static PsiClassType[] getSuperTypes(PsiClass psiClass) {
713     if (psiClass instanceof PsiAnonymousClass) {
714       PsiClassType baseClassType = ((PsiAnonymousClass)psiClass).getBaseClassType();
715       PsiClass baseClass = baseClassType.resolve();
716       if (baseClass == null || !baseClass.isInterface()) {
717         return new PsiClassType[]{baseClassType};
718       }
719       else {
720         PsiClassType objectType = PsiType.getJavaLangObject(psiClass.getManager(), psiClass.getResolveScope());
721         return new PsiClassType[]{objectType, baseClassType};
722       }
723     }
724
725     PsiClassType[] extendsTypes = psiClass.getExtendsListTypes();
726     PsiClassType[] implementsTypes = psiClass.getImplementsListTypes();
727     boolean hasExtends = extendsTypes.length != 0;
728     int extendsListLength = extendsTypes.length + (hasExtends ? 0 : 1);
729     PsiClassType[] result = new PsiClassType[extendsListLength + implementsTypes.length];
730
731     System.arraycopy(extendsTypes, 0, result, 0, extendsTypes.length);
732     if (!hasExtends) {
733       if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) {
734         return PsiClassType.EMPTY_ARRAY;
735       }
736       PsiManager manager = psiClass.getManager();
737       PsiClassType objectType = PsiType.getJavaLangObject(manager, psiClass.getResolveScope());
738       result[0] = objectType;
739     }
740     System.arraycopy(implementsTypes, 0, result, extendsListLength, implementsTypes.length);
741     for (int i = 0; i < result.length; i++) {
742       PsiClassType type = result[i];
743       result[i] = (PsiClassType)PsiUtil.captureToplevelWildcards(type, psiClass);
744     }
745     return result;
746   }
747
748   private static PsiClassType getAnnotationSuperType(PsiClass psiClass, PsiElementFactory factory) {
749     return factory.createTypeByFQClassName("java.lang.annotation.Annotation", psiClass.getResolveScope());
750   }
751
752   private static PsiClassType getEnumSuperType(PsiClass psiClass, PsiElementFactory factory) {
753     PsiClassType superType;
754     final PsiManager manager = psiClass.getManager();
755     final PsiClass enumClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Enum", psiClass.getResolveScope());
756     if (enumClass == null) {
757       try {
758         superType = (PsiClassType)factory.createTypeFromText("java.lang.Enum", null);
759       }
760       catch (IncorrectOperationException e) {
761         superType = null;
762       }
763     }
764     else {
765       final PsiTypeParameter[] typeParameters = enumClass.getTypeParameters();
766       PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
767       if (typeParameters.length == 1) {
768         substitutor = substitutor.put(typeParameters[0], factory.createType(psiClass));
769       }
770       superType = new PsiImmediateClassType(enumClass, substitutor);
771     }
772     return superType;
773   }
774
775   public static PsiClass[] getInterfaces(PsiTypeParameter typeParameter) {
776     final ArrayList<PsiClass> result = new ArrayList<PsiClass>();
777     final PsiClassType[] referencedTypes = typeParameter.getExtendsListTypes();
778     for (PsiClassType referencedType : referencedTypes) {
779       final PsiClass psiClass = referencedType.resolve();
780       if (psiClass != null && psiClass.isInterface()) {
781         result.add(psiClass);
782       }
783     }
784     return result.toArray(new PsiClass[result.size()]);
785   }
786
787   public static PsiClass[] getInterfaces(PsiClass psiClass) {
788     final PsiClassType[] extendsListTypes = psiClass.getExtendsListTypes();
789     if (psiClass.isInterface()) {
790       return resolveClassReferenceList(extendsListTypes, psiClass.getManager(), psiClass.getResolveScope(), false);
791     }
792
793     if (psiClass instanceof PsiAnonymousClass) {
794       PsiClassType baseClassReference = ((PsiAnonymousClass)psiClass).getBaseClassType();
795       PsiClass baseClass = baseClassReference.resolve();
796       if (baseClass != null && baseClass.isInterface()) return new PsiClass[]{baseClass};
797       return PsiClass.EMPTY_ARRAY;
798     }
799
800     final PsiClassType[] implementsListTypes = psiClass.getImplementsListTypes();
801
802     return resolveClassReferenceList(implementsListTypes, psiClass.getManager(), psiClass.getResolveScope(), false);
803   }
804
805   private static PsiClass[] resolveClassReferenceList(final PsiClassType[] listOfTypes,
806                                                       final PsiManager manager, final GlobalSearchScope resolveScope, boolean includeObject)
807   {
808     PsiClass objectClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Object", resolveScope);
809     if (objectClass == null) includeObject = false;
810     if (listOfTypes == null || listOfTypes.length == 0) {
811       if (includeObject) return new PsiClass[]{objectClass};
812       return PsiClass.EMPTY_ARRAY;
813     }
814
815     int referenceCount = listOfTypes.length;
816     if (includeObject) referenceCount++;
817
818     PsiClass[] resolved = new PsiClass[referenceCount];
819     int resolvedCount = 0;
820
821     if (includeObject) resolved[resolvedCount++] = objectClass;
822     for (PsiClassType reference : listOfTypes) {
823       PsiClass refResolved = reference.resolve();
824       if (refResolved != null) resolved[resolvedCount++] = refResolved;
825     }
826
827     if (resolvedCount < referenceCount) {
828       PsiClass[] shorter = new PsiClass[resolvedCount];
829       System.arraycopy(resolved, 0, shorter, 0, resolvedCount);
830       resolved = shorter;
831     }
832
833     return resolved;
834   }
835
836   public static List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(PsiClass psiClass, String name, boolean checkBases) {
837     if (!checkBases) {
838       final PsiMethod[] methodsByName = psiClass.findMethodsByName(name, false);
839       final List<Pair<PsiMethod, PsiSubstitutor>> ret = new ArrayList<Pair<PsiMethod, PsiSubstitutor>>(methodsByName.length);
840       for (final PsiMethod method : methodsByName) {
841         ret.add(new Pair<PsiMethod, PsiSubstitutor>(method, PsiSubstitutor.EMPTY));
842       }
843       return ret;
844     }
845     final Map<String, List<Pair<PsiMethod, PsiSubstitutor>>> map = getMap(psiClass, PsiMethod.class);
846     final List<Pair<PsiMethod, PsiSubstitutor>> list = map.get(name);
847     return list == null ?
848            Collections.<Pair<PsiMethod, PsiSubstitutor>>emptyList() :
849            Collections.unmodifiableList(list);
850   }
851
852   public static PsiClassType[] getExtendsListTypes(PsiClass psiClass) {
853     if (psiClass.isEnum()) {
854       return new PsiClassType[]{getEnumSuperType(psiClass, JavaPsiFacade.getInstance(psiClass.getProject()).getElementFactory())};
855     }
856     else if (psiClass.isAnnotationType()) {
857       return new PsiClassType[]{getAnnotationSuperType(psiClass, JavaPsiFacade.getInstance(psiClass.getProject()).getElementFactory())};
858     }
859     final PsiReferenceList extendsList = psiClass.getExtendsList();
860     if (extendsList != null) {
861       return extendsList.getReferencedTypes();
862     }
863     return PsiClassType.EMPTY_ARRAY;
864   }
865
866   public static PsiClassType[] getImplementsListTypes(PsiClass psiClass) {
867     final PsiReferenceList extendsList = psiClass.getImplementsList();
868     if (extendsList != null) {
869       return extendsList.getReferencedTypes();
870     }
871     return PsiClassType.EMPTY_ARRAY;
872   }
873
874   public static boolean isClassEquivalentTo(PsiClass aClass, PsiElement another) {
875     if (!(another instanceof PsiClass)) return false;
876     String name1 = aClass.getName();
877     if (name1 == null) return false;
878     if (!another.isValid()) return false;
879     String name2 = ((PsiClass)another).getName(); 
880     if (name2 == null) return false;
881     if (name1.hashCode() != name2.hashCode()) return false;
882     if (!name1.equals(name2)) return false;
883     String qName1 = aClass.getQualifiedName();
884     String qName2 = ((PsiClass)another).getQualifiedName();
885     if (qName1 == null || qName2 == null) {
886       //noinspection StringEquality
887       if (qName1 != qName2) return false;
888
889       if (aClass instanceof PsiTypeParameter && another instanceof PsiTypeParameter) {
890         PsiTypeParameter p1 = (PsiTypeParameter)aClass;
891         PsiTypeParameter p2 = (PsiTypeParameter)another;
892
893         return p1.getIndex() == p2.getIndex() &&
894                aClass.getManager().areElementsEquivalent(p1.getOwner(), p2.getOwner());
895
896       }
897       else {
898         return false;
899       }
900     }
901     if (qName1.hashCode() != qName2.hashCode() || !qName1.equals(qName2)) {
902       return false;
903     }
904
905     if (originalElement(aClass).equals(originalElement((PsiClass)another))) {
906       return true;
907     }
908
909     final PsiFile file1 = aClass.getContainingFile().getOriginalFile();
910     final PsiFile file2 = another.getContainingFile().getOriginalFile();
911     if (file1.equals(file2)) {
912       return true;
913     }
914
915     //see com.intellij.openapi.vcs.changes.PsiChangeTracker
916     //see com.intellij.psi.impl.PsiFileFactoryImpl#createFileFromText(CharSequence,PsiFile)
917     final PsiFile original1 = file1.getUserData(PsiFileFactory.ORIGINAL_FILE);
918     final PsiFile original2 = file2.getUserData(PsiFileFactory.ORIGINAL_FILE);
919     if (original1 == original2 && original1 != null
920         || original1 == file2 || original2 == file1) {
921       return true;
922     }    
923
924     final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(aClass.getProject()).getFileIndex();
925     final VirtualFile vfile1 = file1.getViewProvider().getVirtualFile();
926     final VirtualFile vfile2 = file2.getViewProvider().getVirtualFile();
927     return (fileIndex.isInSource(vfile1) || fileIndex.isInLibraryClasses(vfile1)) &&
928            (fileIndex.isInSource(vfile2) || fileIndex.isInLibraryClasses(vfile2));
929   }
930
931   private static PsiElement originalElement(PsiClass aClass) {
932     final PsiElement originalElement = aClass.getOriginalElement();
933     final PsiCompiledElement compiled = originalElement.getUserData(ClsElementImpl.COMPILED_ELEMENT);
934     if (compiled != null) {
935       return compiled;
936     }
937     return originalElement;
938   }
939
940   public static boolean isFieldEquivalentTo(PsiField field, PsiElement another) {
941     if (!(another instanceof PsiField)) return false;
942     String name1 = field.getName();
943     if (name1 == null) return false;
944     if (!another.isValid()) return false;
945
946     String name2 = ((PsiField)another).getName();
947     if (!name1.equals(name2)) return false;
948     PsiClass aClass1 = field.getContainingClass();
949     PsiClass aClass2 = ((PsiField)another).getContainingClass();
950     return aClass1 != null && aClass2 != null && field.getManager().areElementsEquivalent(aClass1, aClass2);
951   }
952
953   public static boolean isMethodEquivalentTo(PsiMethod method1, PsiElement another) {
954     if (!(another instanceof PsiMethod)) return false;
955     PsiMethod method2 = (PsiMethod)another;
956     String name1 = method1.getName();
957     if (!another.isValid()) return false;
958     String name2 = method2.getName();
959     if (!name1.equals(name2)) return false;
960     PsiClass aClass1 = method1.getContainingClass();
961     PsiClass aClass2 = method2.getContainingClass();
962     PsiManager manager = method1.getManager();
963     if (!(aClass1 != null && aClass2 != null && manager.areElementsEquivalent(aClass1, aClass2))) return false;
964
965     PsiParameter[] parameters1 = method1.getParameterList().getParameters();
966     PsiParameter[] parameters2 = method2.getParameterList().getParameters();
967     if (parameters1.length != parameters2.length) return false;
968     for (int i = 0; i < parameters1.length; i++) {
969       PsiParameter parameter1 = parameters1[i];
970       PsiParameter parameter2 = parameters2[i];
971       PsiType type1 = parameter1.getType();
972       PsiType type2 = parameter2.getType();
973       if (!compareParamTypes(manager,type1, type2)) return false;
974     }
975     return true;
976   }
977
978   private static boolean compareParamTypes(@NotNull PsiManager manager, @NotNull PsiType type1, @NotNull PsiType type2) {
979     if (type1 instanceof PsiArrayType) {
980       if (!(type2 instanceof PsiArrayType)) return false;
981       return compareParamTypes(manager, ((PsiArrayType)type1).getComponentType(), ((PsiArrayType)type2).getComponentType());
982     }
983
984     if (!(type1 instanceof PsiClassType) || !(type2 instanceof PsiClassType)) {
985       return type1.equals(type2);
986     }
987
988     PsiClass class1 = ((PsiClassType)type1).resolve();
989     PsiClass class2 = ((PsiClassType)type2).resolve();
990
991     if (class1 instanceof PsiTypeParameter && class2 instanceof PsiTypeParameter) {
992       return Comparing.equal(class1.getName(), class2.getName()) &&
993              ((PsiTypeParameter)class1).getIndex() == ((PsiTypeParameter)class2).getIndex();
994     }
995
996     return manager.areElementsEquivalent(class1, class2);
997   }
998 }