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