IDEA-22821
[fedora-idea.git] / java / java-impl / src / com / intellij / psi / impl / PsiClassImplUtil.java
blob7ce38be3b0c50b82f16032cc4a1e4255f24c0e4c
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.psi.impl;
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.roots.ProjectFileIndex;
20 import com.intellij.openapi.roots.ProjectRootManager;
21 import com.intellij.openapi.util.*;
22 import com.intellij.openapi.vfs.VirtualFile;
23 import com.intellij.psi.*;
24 import com.intellij.psi.filters.OrFilter;
25 import com.intellij.psi.impl.compiled.ClsElementImpl;
26 import com.intellij.psi.impl.source.PsiImmediateClassType;
27 import com.intellij.psi.infos.MethodCandidateInfo;
28 import com.intellij.psi.scope.ElementClassFilter;
29 import com.intellij.psi.scope.ElementClassHint;
30 import com.intellij.psi.scope.NameHint;
31 import com.intellij.psi.scope.PsiScopeProcessor;
32 import com.intellij.psi.scope.processor.FilterScopeProcessor;
33 import com.intellij.psi.scope.processor.MethodResolverProcessor;
34 import com.intellij.psi.search.GlobalSearchScope;
35 import com.intellij.psi.search.LocalSearchScope;
36 import com.intellij.psi.search.PackageScope;
37 import com.intellij.psi.search.SearchScope;
38 import com.intellij.psi.util.*;
39 import com.intellij.ui.IconDeferrer;
40 import com.intellij.ui.RowIcon;
41 import com.intellij.util.Function;
42 import com.intellij.util.IncorrectOperationException;
43 import com.intellij.util.ReflectionCache;
44 import com.intellij.util.SmartList;
45 import com.intellij.util.containers.HashMap;
46 import gnu.trove.THashSet;
47 import org.jetbrains.annotations.NonNls;
48 import org.jetbrains.annotations.NotNull;
49 import org.jetbrains.annotations.Nullable;
51 import javax.swing.*;
52 import java.util.*;
54 /**
55 * Created by IntelliJ IDEA.
56 * User: ik
57 * Date: 24.10.2003
58 * Time: 16:50:37
59 * To change this template use Options | File Templates.
61 public class PsiClassImplUtil {
62 private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiClassImplUtil");
63 private static final Key<Boolean> NAME_MAPS_BUILT_FLAG = Key.create("NAME_MAPS_BUILT_FLAG");
65 private static final Key<CachedValue<Map>> MAP_IN_CLASS_KEY = Key.create("MAP_KEY");
67 @NotNull public static PsiField[] getAllFields(final PsiClass aClass) {
68 List<PsiField> map = getAllByMap(aClass, PsiField.class);
69 return map.toArray(new PsiField[map.size()]);
72 @NotNull public static PsiMethod[] getAllMethods(final PsiClass aClass) {
73 List<PsiMethod> methods = getAllByMap(aClass, PsiMethod.class);
74 return methods.toArray(new PsiMethod[methods.size()]);
77 @NotNull public static PsiClass[] getAllInnerClasses(PsiClass aClass) {
78 List<PsiClass> classes = getAllByMap(aClass, PsiClass.class);
79 return classes.toArray(new PsiClass[classes.size()]);
82 @Nullable public static PsiField findFieldByName(PsiClass aClass, String name, boolean checkBases) {
83 final List<PsiField> byMap = findByMap(aClass, name, checkBases, PsiField.class);
84 return byMap.isEmpty() ? null : byMap.get(0);
87 @NotNull public static PsiMethod[] findMethodsByName(PsiClass aClass, String name, boolean checkBases) {
88 List<PsiMethod> methods = findByMap(aClass, name, checkBases, PsiMethod.class);
89 return methods.toArray(new PsiMethod[methods.size()]);
92 @Nullable public static PsiMethod findMethodBySignature(final PsiClass aClass, final PsiMethod patternMethod, final boolean checkBases) {
93 final List<PsiMethod> result = findMethodsBySignature(aClass, patternMethod, checkBases, true);
94 return result.isEmpty() ? null : result.get(0);
97 // ----------------------------- findMethodsBySignature -----------------------------------
99 @NotNull public static PsiMethod[] findMethodsBySignature(final PsiClass aClass, final PsiMethod patternMethod, final boolean checkBases) {
100 List<PsiMethod> methods = findMethodsBySignature(aClass, patternMethod, checkBases, false);
101 return methods.toArray(new PsiMethod[methods.size()]);
104 @NotNull private static List<PsiMethod> findMethodsBySignature(final PsiClass aClass,
105 final PsiMethod patternMethod,
106 final boolean checkBases,
107 final boolean stopOnFirst) {
108 /* final MethodSignature patternSignature = MethodSignatureBackedByPsiMethod.create(patternMethod, PsiSubstitutor.EMPTY);
109 if (!checkBases) {
110 final PsiMethod[] methodsByName = aClass.findMethodsByName(patternMethod.getName(), false);
111 if (methodsByName.length == 0) return PsiMethod.EMPTY_ARRAY;
112 List<PsiMethod> result = new ArrayList<PsiMethod>();
113 for (PsiMethod method : methodsByName) {
114 final MethodSignature otherSignature = method.getSignature(PsiSubstitutor.EMPTY);
115 if (otherSignature.equals(patternSignature)) {
116 result.add(method);
117 if (stopOnFirst) break;
121 return result.toArray(new PsiMethod[result.size()]);
123 else {
124 final Set<HierarchicalMethodSignature> signatures = getOverrideEquivalentSignatures(aClass);
125 final HierarchicalMethodSignature signatureWithSupers = signatures.get(patternSignature);
126 if (signatureWithSupers == null) return PsiMethod.EMPTY_ARRAY;
127 final List<PsiMethod> result = new ArrayList<PsiMethod>();
128 MethodSignatureUtil.processMethodHierarchy(signatureWithSupers, new Processor<HierarchicalMethodSignature>() {
129 public boolean process(final HierarchicalMethodSignature sig) {
130 result.add(sig.getSignature().getMethod());
131 return !stopOnFirst;
134 return result.toArray(new PsiMethod[result.size()]);
137 final PsiMethod[] methodsByName = aClass.findMethodsByName(patternMethod.getName(), checkBases);
138 if (methodsByName.length == 0) return Collections.emptyList();
139 final List<PsiMethod> methods = new SmartList<PsiMethod>();
140 final MethodSignature patternSignature = patternMethod.getSignature(PsiSubstitutor.EMPTY);
141 for (final PsiMethod method : methodsByName) {
142 final PsiClass superClass = method.getContainingClass();
143 final PsiSubstitutor substitutor;
144 if (checkBases && !aClass.equals(superClass)) {
145 substitutor = TypeConversionUtil.getSuperClassSubstitutor(superClass, aClass, PsiSubstitutor.EMPTY);
147 else {
148 substitutor = PsiSubstitutor.EMPTY;
150 final MethodSignature signature = method.getSignature(substitutor);
151 if (signature.equals(patternSignature)) {
152 methods.add(method);
153 if (stopOnFirst) {
154 break;
158 return methods;
161 // ----------------------------------------------------------------------------------------
163 @Nullable public static PsiClass findInnerByName(PsiClass aClass, String name, boolean checkBases) {
164 final List<PsiClass> byMap = findByMap(aClass, name, checkBases, PsiClass.class);
165 return byMap.isEmpty() ? null : byMap.get(0);
168 @SuppressWarnings({"unchecked"})
169 @NotNull private static <T extends PsiMember> List<T> findByMap(PsiClass aClass, String name, boolean checkBases, Class<T> type) {
170 if (name == null) return Collections.emptyList();
172 if (!checkBases) {
173 T[] members = null;
174 if (ReflectionCache.isAssignable(type,PsiMethod.class)) {
175 members = (T[])aClass.getMethods();
177 else if (ReflectionCache.isAssignable(type,PsiClass.class)) {
178 members = (T[])aClass.getInnerClasses();
180 else if (ReflectionCache.isAssignable(type,PsiField.class)) {
181 members = (T[])aClass.getFields();
183 if (members == null) return Collections.emptyList();
185 List<T> list = new ArrayList<T>();
186 for (T member : members) {
187 if (name.equals(member.getName())) list.add(member);
189 return list;
191 else {
192 final Map<String, List<Pair<T, PsiSubstitutor>>> allMethodsMap = getMap(aClass, type);
193 final List<Pair<T, PsiSubstitutor>> list = allMethodsMap.get(name);
194 if (list == null) return Collections.emptyList();
195 final List<T> ret = new ArrayList<T>();
196 for (final Pair<T, PsiSubstitutor> info : list) {
197 ret.add(info.getFirst());
200 return ret;
204 public static <T extends PsiMember> List<Pair<T, PsiSubstitutor>> getAllWithSubstitutorsByMap(PsiClass aClass, Class<T> type) {
205 final Map<String, List<Pair<T, PsiSubstitutor>>> allMap = getMap(aClass, type);
206 return allMap.get(ALL);
209 @NotNull private static <T extends PsiMember> List<T> getAllByMap(PsiClass aClass, Class<T> type) {
210 List<Pair<T, PsiSubstitutor>> pairs = getAllWithSubstitutorsByMap(aClass, type);
212 assert pairs != null : "pairs should be already computed. Wrong allMap: " + getMap(aClass, type);
214 final List<T> ret = new ArrayList<T>(pairs.size());
215 for (final Pair<T, PsiSubstitutor> pair : pairs) {
216 T t = pair.getFirst();
217 LOG.assertTrue(t != null, aClass);
218 ret.add(t);
220 return ret;
223 @NonNls private static final String ALL = "Intellij-IDEA-ALL";
225 private static Map<Class<? extends PsiMember>, Map<String, List<Pair<PsiMember, PsiSubstitutor>>>> buildAllMaps(final PsiClass psiClass) {
226 final List<Pair<PsiMember, PsiSubstitutor>> classes = new ArrayList<Pair<PsiMember, PsiSubstitutor>>();
227 final List<Pair<PsiMember, PsiSubstitutor>> fields = new ArrayList<Pair<PsiMember, PsiSubstitutor>>();
228 final List<Pair<PsiMember, PsiSubstitutor>> methods = new ArrayList<Pair<PsiMember, PsiSubstitutor>>();
230 FilterScopeProcessor<MethodCandidateInfo> processor = new FilterScopeProcessor<MethodCandidateInfo>(
231 new OrFilter(ElementClassFilter.METHOD, ElementClassFilter.FIELD, ElementClassFilter.CLASS)) {
232 protected void add(PsiElement element, PsiSubstitutor substitutor) {
233 if (element instanceof PsiMethod) {
234 methods.add(new Pair<PsiMember, PsiSubstitutor>((PsiMethod)element, substitutor));
236 else if (element instanceof PsiField) {
237 fields.add(new Pair<PsiMember, PsiSubstitutor>((PsiField)element, substitutor));
239 else if (element instanceof PsiClass) {
240 classes.add(new Pair<PsiMember, PsiSubstitutor>((PsiClass)element, substitutor));
244 PsiElementFactory factory = JavaPsiFacade.getInstance(psiClass.getProject()).getElementFactory();
245 processDeclarationsInClassNotCached(psiClass, processor, ResolveState.initial(), new THashSet<PsiClass>(), null, psiClass, false, factory);
247 Map<Class<? extends PsiMember>, Map<String, List<Pair<PsiMember, PsiSubstitutor>>>> result = new HashMap<Class<? extends PsiMember>, Map<String, List<Pair<PsiMember, PsiSubstitutor>>>>(3);
248 result.put(PsiClass.class, generateMapByList(classes));
249 result.put(PsiMethod.class, generateMapByList(methods));
250 result.put(PsiField.class, generateMapByList(fields));
251 psiClass.putUserData(NAME_MAPS_BUILT_FLAG, Boolean.TRUE);
252 return result;
255 private static Map<String, List<Pair<PsiMember, PsiSubstitutor>>> generateMapByList(@NotNull final List<Pair<PsiMember, PsiSubstitutor>> list) {
256 Map<String, List<Pair<PsiMember, PsiSubstitutor>>> map = new HashMap<String, List<Pair<PsiMember, PsiSubstitutor>>>();
257 map.put(ALL, list);
258 for (final Pair<PsiMember, PsiSubstitutor> info : list) {
259 final PsiMember element = info.getFirst();
260 final String currentName = element.getName();
261 List<Pair<PsiMember, PsiSubstitutor>> listByName = map.get(currentName);
262 if (listByName == null) {
263 listByName = new ArrayList<Pair<PsiMember, PsiSubstitutor>>(1);
264 map.put(currentName, listByName);
266 listByName.add(info);
268 return map;
271 private static <T extends PsiMember> Map<String, List<Pair<T, PsiSubstitutor>>> getMap(final PsiClass aClass, Class<T> memberClazz) {
272 CachedValue<Map> value = aClass.getUserData(MAP_IN_CLASS_KEY);
273 if (value == null) {
274 final CachedValueProvider<Map> provider = new ByNameCachedValueProvider(aClass);
275 value = aClass.getManager().getCachedValuesManager().createCachedValue(provider, false);
276 //Do not cache for nonphysical elements
277 if (aClass.isPhysical()) {
278 value = ((UserDataHolderEx)aClass).putUserDataIfAbsent(MAP_IN_CLASS_KEY, value);
281 return (Map<String, List<Pair<T, PsiSubstitutor>>>)value.getValue().get(memberClazz);
284 private static class ClassIconRequest {
285 public PsiClass psiClass;
286 public int flags;
288 private ClassIconRequest(PsiClass psiClass, int flags) {
289 this.psiClass = psiClass;
290 this.flags = flags;
293 @Override
294 public boolean equals(Object o) {
295 if (this == o) return true;
296 if (!(o instanceof ClassIconRequest)) return false;
298 ClassIconRequest that = (ClassIconRequest)o;
300 if (flags != that.flags) return false;
301 if (psiClass != null ? !psiClass.equals(that.psiClass) : that.psiClass != null) return false;
303 return true;
306 @Override
307 public int hashCode() {
308 int result = psiClass != null ? psiClass.hashCode() : 0;
309 result = 31 * result + flags;
310 return result;
314 private static final Function<ClassIconRequest, Icon> FULL_ICON_EVALUATOR = new Function<ClassIconRequest, Icon>() {
315 public Icon fun(ClassIconRequest r) {
316 if (!r.psiClass.isValid() || r.psiClass.getProject().isDisposed()) return null;
318 final boolean isLocked = (r.flags & Iconable.ICON_FLAG_READ_STATUS) != 0 && !r.psiClass.isWritable();
319 Icon symbolIcon = ElementPresentationUtil.getClassIconOfKind(r.psiClass, ElementPresentationUtil.getClassKind(r.psiClass));
320 RowIcon baseIcon = ElementBase.createLayeredIcon(symbolIcon, ElementPresentationUtil.getFlags(r.psiClass, isLocked));
321 return ElementPresentationUtil.addVisibilityIcon(r.psiClass, r.flags, baseIcon);
325 public static Icon getClassIcon(final int flags, final PsiClass aClass) {
326 Icon symbolIcon = ElementPresentationUtil.getClassIconOfKind(aClass, ElementPresentationUtil.getBasicClassKind(aClass));
327 RowIcon baseIcon = ElementBase.createLayeredIcon(symbolIcon, 0);
329 return IconDeferrer.getInstance().defer(ElementPresentationUtil.addVisibilityIcon(aClass, flags, baseIcon),
330 new ClassIconRequest(aClass, flags),
331 FULL_ICON_EVALUATOR);
334 public static SearchScope getClassUseScope(final PsiClass aClass) {
335 final GlobalSearchScope maximalUseScope = ((PsiManagerEx) aClass.getManager()).getFileManager().getUseScope(aClass);
336 if (aClass instanceof PsiAnonymousClass) {
337 return new LocalSearchScope(aClass);
339 PsiFile file = aClass.getContainingFile();
340 if (JspPsiUtil.isInJspFile(file)) return maximalUseScope;
341 final PsiClass containingClass = aClass.getContainingClass();
342 if (aClass.hasModifierProperty(PsiModifier.PUBLIC)) {
343 return containingClass != null ? containingClass.getUseScope() : maximalUseScope;
345 else if (aClass.hasModifierProperty(PsiModifier.PROTECTED)) {
346 return containingClass != null ? containingClass.getUseScope() : maximalUseScope;
348 else if (aClass.hasModifierProperty(PsiModifier.PRIVATE) || aClass instanceof PsiTypeParameter) {
349 PsiClass topClass = PsiUtil.getTopLevelClass(aClass);
350 return new LocalSearchScope(topClass == null ? aClass.getContainingFile() : topClass);
352 else {
353 PsiPackage aPackage = null;
354 if (file instanceof PsiJavaFile) {
355 aPackage = JavaPsiFacade.getInstance(aClass.getProject()).findPackage(((PsiJavaFile)file).getPackageName());
358 if (aPackage == null) {
359 PsiDirectory dir = file.getContainingDirectory();
360 if (dir != null) {
361 aPackage = JavaDirectoryService.getInstance().getPackage(dir);
365 if (aPackage != null) {
366 SearchScope scope = PackageScope.packageScope(aPackage, false);
367 scope = scope.intersectWith(maximalUseScope);
368 return scope;
371 return new LocalSearchScope(file);
375 public static boolean isMainMethod(PsiMethod method) {
376 if (!PsiType.VOID.equals(method.getReturnType())) return false;
377 PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
378 try {
379 PsiMethod appMain = factory.createMethodFromText("void main(String[] args);", null);
380 if (MethodSignatureUtil.areSignaturesEqual(method, appMain)) return true;
381 PsiMethod appPremain = factory.createMethodFromText("void premain(String args, java.lang.instrument.Instrumentation i);", null);
382 if (MethodSignatureUtil.areSignaturesEqual(method, appPremain)) return true;
384 catch (IncorrectOperationException e) {
385 LOG.error(e);
387 return false;
390 private static class ByNameCachedValueProvider implements CachedValueProvider<Map> {
391 private final PsiClass myClass;
393 private ByNameCachedValueProvider(final PsiClass aClass) {
394 myClass = aClass;
397 public Result<Map> compute() {
398 final Map<Class<? extends PsiMember>, Map<String, List<Pair<PsiMember, PsiSubstitutor>>>> map = buildAllMaps(myClass);
399 return new Result<Map>(map, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
403 public static boolean processDeclarationsInClass(PsiClass aClass,
404 PsiScopeProcessor processor,
405 ResolveState state,
406 Set<PsiClass> visited,
407 PsiElement last,
408 PsiElement place,
409 boolean isRaw) {
410 if (visited != null && visited.contains(aClass)) return true;
411 PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
412 isRaw = isRaw || PsiUtil.isRawSubstitutor(aClass, substitutor);
413 if (last instanceof PsiTypeParameterList || last instanceof PsiModifierList) return true; //TypeParameterList and ModifierList do not see our declarations
414 final Boolean built = aClass.getUserData(NAME_MAPS_BUILT_FLAG);
415 PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory();
416 if (built == null) {
417 return processDeclarationsInClassNotCached(aClass, processor, state, visited, last, place, isRaw, factory);
420 final NameHint nameHint = processor.getHint(NameHint.KEY);
421 final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
423 if (nameHint != null) {
424 if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.FIELD)) {
425 final PsiField fieldByName = aClass.findFieldByName(nameHint.getName(state), false);
426 if (fieldByName != null) {
427 processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
428 if (!processor.execute(fieldByName, state)) return false;
430 else {
431 final Map<String, List<Pair<PsiField, PsiSubstitutor>>> allFieldsMap = getMap(aClass, PsiField.class);
433 final List<Pair<PsiField, PsiSubstitutor>> list = allFieldsMap.get(nameHint.getName(state));
434 if (list != null) {
435 for (final Pair<PsiField, PsiSubstitutor> candidate : list) {
436 PsiField candidateField = candidate.getFirst();
437 PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(candidateField.getContainingClass(), candidate.getSecond(), aClass,
438 substitutor, place, factory);
440 processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, candidateField.getContainingClass());
441 if (!processor.execute(candidateField, state.put(PsiSubstitutor.KEY, finalSubstitutor))) return false;
446 if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.CLASS)) {
447 if (last != null && last.getParent() == aClass) {
448 if (last instanceof PsiClass) {
449 if (!processor.execute(last, state)) return false;
451 // Parameters
452 final PsiTypeParameterList list = aClass.getTypeParameterList();
453 if (list != null && !list.processDeclarations(processor, state, last, place)) return false;
455 if (!(last instanceof PsiReferenceList)) {
456 final PsiClass classByName = aClass.findInnerClassByName(nameHint.getName(state), false);
457 if (classByName != null) {
458 processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
459 if (!processor.execute(classByName, state)) return false;
461 else {
462 final Map<String, List<Pair<PsiClass, PsiSubstitutor>>> allClassesMap = getMap(aClass, PsiClass.class);
464 final List<Pair<PsiClass, PsiSubstitutor>> list = allClassesMap.get(nameHint.getName(state));
465 if (list != null) {
466 for (final Pair<PsiClass, PsiSubstitutor> candidate : list) {
467 final PsiClass inner = candidate.getFirst();
468 final PsiClass containingClass = inner.getContainingClass();
469 if (containingClass != null) {
470 PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(containingClass, candidate.getSecond(), aClass,
471 substitutor, place, factory);
472 processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass);
473 if (!processor.execute(inner, state.put(PsiSubstitutor.KEY, finalSubstitutor))) return false;
480 if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.METHOD)) {
481 if (processor instanceof MethodResolverProcessor) {
482 final MethodResolverProcessor methodResolverProcessor = (MethodResolverProcessor)processor;
483 if (methodResolverProcessor.isConstructor()) {
484 final PsiMethod[] constructors = aClass.getConstructors();
485 methodResolverProcessor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
486 for (PsiMethod constructor : constructors) {
487 if (!methodResolverProcessor.execute(constructor, state)) return false;
489 return true;
492 final Map<String, List<Pair<PsiMethod, PsiSubstitutor>>> allMethodsMap = getMap(aClass, PsiMethod.class);
493 final List<Pair<PsiMethod, PsiSubstitutor>> list = allMethodsMap.get(nameHint.getName(state));
494 if (list != null) {
495 for (final Pair<PsiMethod, PsiSubstitutor> candidate : list) {
496 PsiMethod candidateMethod = candidate.getFirst();
497 if (processor instanceof MethodResolverProcessor) {
498 if (candidateMethod.isConstructor() != ((MethodResolverProcessor)processor).isConstructor()) continue;
500 final PsiClass containingClass = candidateMethod.getContainingClass();
501 PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(containingClass, candidate.getSecond(), aClass,
502 substitutor, place, factory);
503 if (isRaw && !candidateMethod.hasModifierProperty(PsiModifier.STATIC)) { //static methods are not erased due to raw overriding
504 PsiTypeParameter[] methodTypeParameters = candidateMethod.getTypeParameters();
505 finalSubstitutor = factory.createRawSubstitutor(finalSubstitutor, methodTypeParameters);
507 processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass);
508 if (!processor.execute(candidateMethod, state.put(PsiSubstitutor.KEY, finalSubstitutor))) return false;
512 return true;
515 return processDeclarationsInClassNotCached(aClass, processor, state, visited, last, place, isRaw, factory);
518 private static PsiSubstitutor obtainFinalSubstitutor(@NotNull PsiClass candidateClass, PsiSubstitutor candidateSubstitutor, PsiClass aClass,
519 PsiSubstitutor substitutor,
520 final PsiElement place,
521 PsiElementFactory elementFactory) {
522 if (PsiUtil.isRawSubstitutor(aClass, substitutor)) {
523 return elementFactory.createRawSubstitutor(candidateClass);
526 final PsiType containingType = elementFactory.createType(candidateClass, candidateSubstitutor, PsiUtil.getLanguageLevel(place));
527 PsiType type = substitutor.substitute(containingType);
528 if (!(type instanceof PsiClassType)) return candidateSubstitutor;
529 return ((PsiClassType)type).resolveGenerics().getSubstitutor();
532 private static boolean processDeclarationsInClassNotCached(PsiClass aClass, PsiScopeProcessor processor, ResolveState state, Set<PsiClass> visited,
533 PsiElement last,
534 PsiElement place,
535 boolean isRaw,
536 PsiElementFactory factory) {
537 if (visited == null) visited = new THashSet<PsiClass>();
538 if (!visited.add(aClass)) return true;
539 processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
540 final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
541 final NameHint nameHint = processor.getHint(NameHint.KEY);
544 if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.FIELD)) {
545 if (nameHint != null) {
546 final PsiField fieldByName = aClass.findFieldByName(nameHint.getName(state), false);
547 if (fieldByName != null) {
548 if (!processor.execute(fieldByName, state)) return false;
551 else {
552 final PsiField[] fields = aClass.getFields();
553 for (final PsiField field : fields) {
554 if (!processor.execute(field, state)) return false;
559 if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.METHOD)) {
560 final PsiMethod[] methods = nameHint != null ? aClass.findMethodsByName(nameHint.getName(state), false) : aClass.getMethods();
561 for (final PsiMethod method : methods) {
562 if (isRaw && !method.hasModifierProperty(PsiModifier.STATIC)) { //static methods are not erased due to raw overriding
563 PsiTypeParameter[] methodTypeParameters = method.getTypeParameters();
564 PsiSubstitutor raw = factory.createRawSubstitutor(state.get(PsiSubstitutor.KEY), methodTypeParameters);
565 state = state.put(PsiSubstitutor.KEY, raw);
567 if (!processor.execute(method, state)) return false;
571 if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.CLASS)) {
572 if (last != null && last.getParent() == aClass) {
573 // Parameters
574 final PsiTypeParameterList list = aClass.getTypeParameterList();
575 if (list != null && !list.processDeclarations(processor, ResolveState.initial(), last, place)) return false;
578 if (!(last instanceof PsiReferenceList) && !(last instanceof PsiModifierList)) {
579 // Inners
580 if (nameHint != null) {
581 final PsiClass inner = aClass.findInnerClassByName(nameHint.getName(state), false);
582 if (inner != null) {
583 if (!processor.execute(inner, state)) return false;
586 else {
587 final PsiClass[] inners = aClass.getInnerClasses();
588 for (final PsiClass inner : inners) {
589 if (!processor.execute(inner, state)) return false;
595 return last instanceof PsiReferenceList || processSuperTypes(aClass, processor, visited, last, place, state, isRaw, factory);
598 private static boolean processSuperTypes(PsiClass aClass,
599 PsiScopeProcessor processor,
600 Set<PsiClass> visited,
601 PsiElement last,
602 PsiElement place,
603 ResolveState state,
604 boolean isRaw, PsiElementFactory factory) {
605 for (final PsiClassType superType : aClass.getSuperTypes()) {
606 final PsiClassType.ClassResolveResult superTypeResolveResult = superType.resolveGenerics();
607 PsiClass superClass = superTypeResolveResult.getElement();
608 if (superClass == null) continue;
609 PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(superClass, superTypeResolveResult.getSubstitutor(), aClass, state.get(PsiSubstitutor.KEY),
610 place, factory);
611 if (!processDeclarationsInClass(superClass, processor, state.put(PsiSubstitutor.KEY, finalSubstitutor), visited, last, place, isRaw)) {
612 return false;
615 return true;
618 @Nullable
619 public static PsiClass getSuperClass(PsiClass psiClass) {
620 PsiManager manager = psiClass.getManager();
621 GlobalSearchScope resolveScope = psiClass.getResolveScope();
623 final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
624 if (psiClass.isInterface()) {
625 return facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope);
627 if (psiClass.isEnum()) {
628 return facade.findClass(CommonClassNames.JAVA_LANG_ENUM, resolveScope);
631 if (psiClass instanceof PsiAnonymousClass) {
632 PsiClassType baseClassReference = ((PsiAnonymousClass)psiClass).getBaseClassType();
633 PsiClass baseClass = baseClassReference.resolve();
634 if (baseClass == null || baseClass.isInterface()) return facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope);
635 return baseClass;
638 if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) return null;
640 final PsiClassType[] referenceElements = psiClass.getExtendsListTypes();
642 if (referenceElements.length == 0) return facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope);
644 PsiClass psiResoved = referenceElements[0].resolve();
645 return psiResoved == null ? facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope) : psiResoved;
648 @NotNull public static PsiClass[] getSupers(PsiClass psiClass) {
649 final PsiClass[] supers = getSupersInner(psiClass);
650 for (final PsiClass aSuper : supers) {
651 LOG.assertTrue(aSuper != null);
653 return supers;
656 private static PsiClass[] getSupersInner(PsiClass psiClass) {
657 PsiClassType[] extendsListTypes = psiClass.getExtendsListTypes();
658 PsiClassType[] implementsListTypes = psiClass.getImplementsListTypes();
660 if (psiClass.isInterface()) {
661 return resolveClassReferenceList(extendsListTypes,
662 psiClass.getManager(), psiClass.getResolveScope(), true);
665 if (psiClass instanceof PsiAnonymousClass) {
666 PsiAnonymousClass psiAnonymousClass = (PsiAnonymousClass)psiClass;
667 PsiClassType baseClassReference = psiAnonymousClass.getBaseClassType();
668 PsiClass baseClass = baseClassReference.resolve();
669 if (baseClass != null) {
670 if (baseClass.isInterface()) {
671 PsiClass objectClass = JavaPsiFacade.getInstance(psiClass.getProject()).findClass("java.lang.Object", psiClass.getResolveScope());
672 return objectClass != null ? new PsiClass[]{objectClass, baseClass} : new PsiClass[]{baseClass};
674 return new PsiClass[]{baseClass};
677 PsiClass objectClass = JavaPsiFacade.getInstance(psiClass.getProject()).findClass("java.lang.Object", psiClass.getResolveScope());
678 return objectClass != null ? new PsiClass[]{objectClass} : PsiClass.EMPTY_ARRAY;
680 else if (psiClass instanceof PsiTypeParameter) {
681 if (extendsListTypes.length == 0) {
682 final PsiClass objectClass =
683 JavaPsiFacade.getInstance(psiClass.getProject()).findClass("java.lang.Object", psiClass.getResolveScope());
684 return objectClass != null ? new PsiClass[]{objectClass} : PsiClass.EMPTY_ARRAY;
686 return resolveClassReferenceList(extendsListTypes, psiClass.getManager(),
687 psiClass.getResolveScope(), false);
690 PsiClass[] interfaces = resolveClassReferenceList(implementsListTypes, psiClass.getManager(), psiClass.getResolveScope(), false);
692 PsiClass superClass = getSuperClass(psiClass);
693 if (superClass == null) return interfaces;
694 PsiClass[] types = new PsiClass[interfaces.length + 1];
695 types[0] = superClass;
696 System.arraycopy(interfaces, 0, types, 1, interfaces.length);
698 return types;
701 @NotNull public static PsiClassType[] getSuperTypes(PsiClass psiClass) {
702 if (psiClass instanceof PsiAnonymousClass) {
703 PsiClassType baseClassType = ((PsiAnonymousClass)psiClass).getBaseClassType();
704 PsiClass baseClass = baseClassType.resolve();
705 if (baseClass == null || !baseClass.isInterface()) {
706 return new PsiClassType[]{baseClassType};
708 else {
709 PsiClassType objectType = PsiType.getJavaLangObject(psiClass.getManager(), psiClass.getResolveScope());
710 return new PsiClassType[]{objectType, baseClassType};
714 PsiClassType[] extendsTypes = psiClass.getExtendsListTypes();
715 PsiClassType[] implementsTypes = psiClass.getImplementsListTypes();
716 boolean hasExtends = extendsTypes.length != 0;
717 int extendsListLength = extendsTypes.length + (hasExtends ? 0 : 1);
718 PsiClassType[] result = new PsiClassType[extendsListLength + implementsTypes.length];
720 System.arraycopy(extendsTypes, 0, result, 0, extendsTypes.length);
721 if (!hasExtends) {
722 if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) {
723 return PsiClassType.EMPTY_ARRAY;
725 PsiManager manager = psiClass.getManager();
726 PsiClassType objectType = PsiType.getJavaLangObject(manager, psiClass.getResolveScope());
727 result[0] = objectType;
729 System.arraycopy(implementsTypes, 0, result, extendsListLength, implementsTypes.length);
730 for (int i = 0; i < result.length; i++) {
731 PsiClassType type = result[i];
732 result[i] = (PsiClassType)PsiUtil.captureToplevelWildcards(type, psiClass);
734 return result;
737 private static PsiClassType getAnnotationSuperType(PsiClass psiClass, PsiElementFactory factory) {
738 return factory.createTypeByFQClassName("java.lang.annotation.Annotation", psiClass.getResolveScope());
741 private static PsiClassType getEnumSuperType(PsiClass psiClass, PsiElementFactory factory) {
742 PsiClassType superType;
743 final PsiManager manager = psiClass.getManager();
744 final PsiClass enumClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Enum", psiClass.getResolveScope());
745 if (enumClass == null) {
746 try {
747 superType = (PsiClassType)factory.createTypeFromText("java.lang.Enum", null);
749 catch (IncorrectOperationException e) {
750 superType = null;
753 else {
754 final PsiTypeParameter[] typeParameters = enumClass.getTypeParameters();
755 PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
756 if (typeParameters.length == 1) {
757 substitutor = substitutor.put(typeParameters[0], factory.createType(psiClass));
759 superType = new PsiImmediateClassType(enumClass, substitutor);
761 return superType;
764 public static PsiClass[] getInterfaces(PsiTypeParameter typeParameter) {
765 final ArrayList<PsiClass> result = new ArrayList<PsiClass>();
766 final PsiClassType[] referencedTypes = typeParameter.getExtendsListTypes();
767 for (PsiClassType referencedType : referencedTypes) {
768 final PsiClass psiClass = referencedType.resolve();
769 if (psiClass != null && psiClass.isInterface()) {
770 result.add(psiClass);
773 return result.toArray(new PsiClass[result.size()]);
776 public static PsiClass[] getInterfaces(PsiClass psiClass) {
777 final PsiClassType[] extendsListTypes = psiClass.getExtendsListTypes();
778 if (psiClass.isInterface()) {
779 return resolveClassReferenceList(extendsListTypes, psiClass.getManager(), psiClass.getResolveScope(), false);
782 if (psiClass instanceof PsiAnonymousClass) {
783 PsiClassType baseClassReference = ((PsiAnonymousClass)psiClass).getBaseClassType();
784 PsiClass baseClass = baseClassReference.resolve();
785 if (baseClass != null && baseClass.isInterface()) return new PsiClass[]{baseClass};
786 return PsiClass.EMPTY_ARRAY;
789 final PsiClassType[] implementsListTypes = psiClass.getImplementsListTypes();
791 return resolveClassReferenceList(implementsListTypes, psiClass.getManager(), psiClass.getResolveScope(), false);
794 private static PsiClass[] resolveClassReferenceList(final PsiClassType[] listOfTypes,
795 final PsiManager manager, final GlobalSearchScope resolveScope, boolean includeObject)
797 PsiClass objectClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Object", resolveScope);
798 if (objectClass == null) includeObject = false;
799 if (listOfTypes == null || listOfTypes.length == 0) {
800 if (includeObject) return new PsiClass[]{objectClass};
801 return PsiClass.EMPTY_ARRAY;
804 int referenceCount = listOfTypes.length;
805 if (includeObject) referenceCount++;
807 PsiClass[] resolved = new PsiClass[referenceCount];
808 int resolvedCount = 0;
810 if (includeObject) resolved[resolvedCount++] = objectClass;
811 for (PsiClassType reference : listOfTypes) {
812 PsiClass refResolved = reference.resolve();
813 if (refResolved != null) resolved[resolvedCount++] = refResolved;
816 if (resolvedCount < referenceCount) {
817 PsiClass[] shorter = new PsiClass[resolvedCount];
818 System.arraycopy(resolved, 0, shorter, 0, resolvedCount);
819 resolved = shorter;
822 return resolved;
825 public static List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(PsiClass psiClass, String name, boolean checkBases) {
826 if (!checkBases) {
827 final PsiMethod[] methodsByName = psiClass.findMethodsByName(name, false);
828 final List<Pair<PsiMethod, PsiSubstitutor>> ret = new ArrayList<Pair<PsiMethod, PsiSubstitutor>>(methodsByName.length);
829 for (final PsiMethod method : methodsByName) {
830 ret.add(new Pair<PsiMethod, PsiSubstitutor>(method, PsiSubstitutor.EMPTY));
832 return ret;
834 final Map<String, List<Pair<PsiMethod, PsiSubstitutor>>> map = getMap(psiClass, PsiMethod.class);
835 final List<Pair<PsiMethod, PsiSubstitutor>> list = map.get(name);
836 return list == null ?
837 Collections.<Pair<PsiMethod, PsiSubstitutor>>emptyList() :
838 Collections.unmodifiableList(list);
841 public static PsiClassType[] getExtendsListTypes(PsiClass psiClass) {
842 if (psiClass.isEnum()) {
843 return new PsiClassType[]{getEnumSuperType(psiClass, JavaPsiFacade.getInstance(psiClass.getProject()).getElementFactory())};
845 else if (psiClass.isAnnotationType()) {
846 return new PsiClassType[]{getAnnotationSuperType(psiClass, JavaPsiFacade.getInstance(psiClass.getProject()).getElementFactory())};
848 final PsiReferenceList extendsList = psiClass.getExtendsList();
849 if (extendsList != null) {
850 return extendsList.getReferencedTypes();
852 return PsiClassType.EMPTY_ARRAY;
855 public static PsiClassType[] getImplementsListTypes(PsiClass psiClass) {
856 final PsiReferenceList extendsList = psiClass.getImplementsList();
857 if (extendsList != null) {
858 return extendsList.getReferencedTypes();
860 return PsiClassType.EMPTY_ARRAY;
863 public static boolean isClassEquivalentTo(PsiClass aClass, PsiElement another) {
864 if (!(another instanceof PsiClass)) return false;
865 String name1 = aClass.getName();
866 if (name1 == null) return false;
867 if (!another.isValid()) return false;
868 String name2 = ((PsiClass)another).getName();
869 if (name2 == null) return false;
870 if (name1.hashCode() != name2.hashCode()) return false;
871 if (!name1.equals(name2)) return false;
872 String qName1 = aClass.getQualifiedName();
873 String qName2 = ((PsiClass)another).getQualifiedName();
874 if (qName1 == null || qName2 == null) {
875 //noinspection StringEquality
876 if (qName1 != qName2) return false;
878 if (aClass instanceof PsiTypeParameter && another instanceof PsiTypeParameter) {
879 PsiTypeParameter p1 = (PsiTypeParameter)aClass;
880 PsiTypeParameter p2 = (PsiTypeParameter)another;
882 return p1.getIndex() == p2.getIndex() &&
883 aClass.getManager().areElementsEquivalent(p1.getOwner(), p2.getOwner());
886 else {
887 return false;
890 if (qName1.hashCode() != qName2.hashCode() || !qName1.equals(qName2)) {
891 return false;
894 if (originalElement(aClass).equals(originalElement((PsiClass)another))) {
895 return true;
898 final PsiFile file1 = aClass.getContainingFile().getOriginalFile();
899 final PsiFile file2 = another.getContainingFile().getOriginalFile();
900 if (file1.equals(file2)) {
901 return true;
904 //see com.intellij.openapi.vcs.changes.PsiChangeTracker
905 //see com.intellij.psi.impl.PsiFileFactoryImpl#createFileFromText(CharSequence,PsiFile)
906 final PsiFile original1 = file1.getUserData(PsiFileFactory.ORIGINAL_FILE);
907 final PsiFile original2 = file2.getUserData(PsiFileFactory.ORIGINAL_FILE);
908 if (original1 == original2 && original1 != null
909 || original1 == file2 || original2 == file1) {
910 return true;
913 final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(aClass.getProject()).getFileIndex();
914 final VirtualFile vfile1 = file1.getViewProvider().getVirtualFile();
915 final VirtualFile vfile2 = file2.getViewProvider().getVirtualFile();
916 return (fileIndex.isInSource(vfile1) || fileIndex.isInLibraryClasses(vfile1)) &&
917 (fileIndex.isInSource(vfile2) || fileIndex.isInLibraryClasses(vfile2));
920 private static PsiElement originalElement(PsiClass aClass) {
921 final PsiElement originalElement = aClass.getOriginalElement();
922 final PsiCompiledElement compiled = originalElement.getUserData(ClsElementImpl.COMPILED_ELEMENT);
923 if (compiled != null) {
924 return compiled;
926 return originalElement;
929 public static boolean isFieldEquivalentTo(PsiField field, PsiElement another) {
930 if (!(another instanceof PsiField)) return false;
931 String name1 = field.getName();
932 if (name1 == null) return false;
933 if (!another.isValid()) return false;
935 String name2 = ((PsiField)another).getName();
936 if (!name1.equals(name2)) return false;
937 PsiClass aClass1 = field.getContainingClass();
938 PsiClass aClass2 = ((PsiField)another).getContainingClass();
939 return aClass1 != null && aClass2 != null && field.getManager().areElementsEquivalent(aClass1, aClass2);
942 public static boolean isMethodEquivalentTo(PsiMethod method1, PsiElement another) {
943 if (!(another instanceof PsiMethod)) return false;
944 PsiMethod method2 = (PsiMethod)another;
945 String name1 = method1.getName();
946 if (!another.isValid()) return false;
947 String name2 = method2.getName();
948 if (!name1.equals(name2)) return false;
949 PsiClass aClass1 = method1.getContainingClass();
950 PsiClass aClass2 = method2.getContainingClass();
951 PsiManager manager = method1.getManager();
952 if (!(aClass1 != null && aClass2 != null && manager.areElementsEquivalent(aClass1, aClass2))) return false;
954 PsiParameter[] parameters1 = method1.getParameterList().getParameters();
955 PsiParameter[] parameters2 = method2.getParameterList().getParameters();
956 if (parameters1.length != parameters2.length) return false;
957 for (int i = 0; i < parameters1.length; i++) {
958 PsiParameter parameter1 = parameters1[i];
959 PsiParameter parameter2 = parameters2[i];
960 PsiType type1 = parameter1.getType();
961 PsiType type2 = parameter2.getType();
962 if (!(type1 instanceof PsiClassType) || !(type2 instanceof PsiClassType)) {
963 if (!type1.equals(type2)) return false;
965 else {
966 PsiClass class1 = ((PsiClassType)type1).resolve();
967 PsiClass class2 = ((PsiClassType)type2).resolve();
969 if (class1 instanceof PsiTypeParameter && class2 instanceof PsiTypeParameter) {
970 return Comparing.equal(class1.getName(), class2.getName()) &&
971 ((PsiTypeParameter)class1).getIndex() == ((PsiTypeParameter)class2).getIndex();
974 if (!manager.areElementsEquivalent(class1, class2)) return false;
977 return true;