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