record Ctrl-F12 as navigation (IDEA-22870)
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / resolve / CollectClassMembersUtil.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.plugins.groovy.lang.resolve;
17
18 import com.intellij.openapi.util.Key;
19 import com.intellij.openapi.util.Trinity;
20 import com.intellij.psi.*;
21 import com.intellij.psi.infos.CandidateInfo;
22 import com.intellij.psi.util.*;
23 import com.intellij.util.containers.HashMap;
24 import com.intellij.util.containers.HashSet;
25 import org.jetbrains.annotations.NotNull;
26 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
27
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32
33 /**
34  * @author ven
35  */
36 public class CollectClassMembersUtil {
37   private static final Key<CachedValue<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>>> CACHED_MEMBERS = Key.create("CACHED_CLASS_MEMBERS");
38
39   private static final Key<CachedValue<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>>> CACHED_MEMBERS_INCLUDING_SYNTHETIC = Key.create("CACHED_MEMBERS_INCLUDING_SYNTHETIC");
40
41   private CollectClassMembersUtil() {
42   }
43
44
45   public static Map<String, List<CandidateInfo>> getAllMethods(final PsiClass aClass, boolean includeSynthetic) {
46     return getCachedMembers(aClass, includeSynthetic).getSecond();
47   }
48
49   @NotNull
50   private static Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>> getCachedMembers(
51     PsiClass aClass,
52     boolean includeSynthetic) {
53     Key<CachedValue<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>>> key =
54       includeSynthetic ? CACHED_MEMBERS_INCLUDING_SYNTHETIC : CACHED_MEMBERS;
55     CachedValue<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>> cachedValue = aClass.getUserData(key);
56     if (cachedValue == null) {
57       cachedValue = buildCache(aClass, includeSynthetic);
58       aClass.putUserData(key, cachedValue);
59     }
60     return cachedValue.getValue();
61   }
62
63   public static Map<String, CandidateInfo> getAllInnerClasses(final PsiClass aClass, boolean includeSynthetic) {
64     return getCachedMembers(aClass, includeSynthetic).getThird();
65   }
66
67   public static Map<String, CandidateInfo> getAllFields(final PsiClass aClass) {
68     return getCachedMembers(aClass, false).getFirst();
69   }
70
71   private static CachedValue<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>> buildCache(final PsiClass aClass, final boolean includeSynthetic) {
72     return CachedValuesManager.getManager(aClass.getProject()).createCachedValue(new CachedValueProvider<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>>() {
73       public Result<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>> compute() {
74         Map<String, CandidateInfo> allFields = new HashMap<String, CandidateInfo>();
75         Map<String, List<CandidateInfo>> allMethods = new HashMap<String, List<CandidateInfo>>();
76         Map<String, CandidateInfo> allInnerClasses = new HashMap<String, CandidateInfo>();
77
78         processClass(aClass, allFields, allMethods, allInnerClasses, new HashSet<PsiClass>(), PsiSubstitutor.EMPTY, includeSynthetic);
79         return Result.create(Trinity.create(allFields, allMethods, allInnerClasses), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
80       }
81     }, false);
82   }
83
84   private static void processClass(PsiClass aClass, Map<String, CandidateInfo> allFields, Map<String, List<CandidateInfo>> allMethods, Map<String, CandidateInfo> allInnerClasses, Set<PsiClass> visitedClasses, PsiSubstitutor substitutor, boolean includeSynthetic) {
85     if (visitedClasses.contains(aClass)) return;
86     visitedClasses.add(aClass);
87
88     for (PsiField field : aClass.getFields()) {
89       String name = field.getName();
90       if (!allFields.containsKey(name)) {
91         allFields.put(name, new CandidateInfo(field, substitutor));
92       }
93     }
94
95     for (PsiMethod method : includeSynthetic || !(aClass instanceof GrTypeDefinition) ? aClass.getMethods() : ((GrTypeDefinition) aClass).getGroovyMethods()) {
96       addMethod(allMethods, method, substitutor);
97     }
98
99     for (final PsiClass inner : aClass.getInnerClasses()) {
100       final String name = inner.getName();
101       if (name != null && !allInnerClasses.containsKey(name)) {
102         allInnerClasses.put(name, new CandidateInfo(inner, substitutor));
103       }
104     }
105
106     for (PsiClassType superType : aClass.getSuperTypes()) {
107       PsiClass superClass = superType.resolve();
108       if (superClass != null) {
109         final PsiSubstitutor superSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(superClass, aClass, substitutor);
110         processClass(superClass, allFields, allMethods, allInnerClasses, visitedClasses, superSubstitutor, includeSynthetic);
111       }
112     }
113   }
114
115   private static void addMethod(Map<String, List<CandidateInfo>> allMethods, PsiMethod method, PsiSubstitutor substitutor) {
116     String name = method.getName();
117     List<CandidateInfo> methods = allMethods.get(name);
118     if (methods == null) {
119       methods = new ArrayList<CandidateInfo>();
120       allMethods.put(name, methods);
121       methods.add(new CandidateInfo(method, substitutor));
122     } else methods.add(new CandidateInfo(method, substitutor));
123   }
124 }