type inference for closures in groovy1.8
[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.auxiliary.modifiers.GrModifierList;
27 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
28 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
29
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35 /**
36  * @author ven
37  */
38 public class CollectClassMembersUtil {
39   private static final Key<CachedValue<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>>> CACHED_MEMBERS = Key.create("CACHED_CLASS_MEMBERS");
40
41   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");
42
43   private CollectClassMembersUtil() {
44   }
45
46
47   public static Map<String, List<CandidateInfo>> getAllMethods(final PsiClass aClass, boolean includeSynthetic) {
48     return getCachedMembers(aClass, includeSynthetic).getSecond();
49   }
50
51   @NotNull
52   private static Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>> getCachedMembers(
53     PsiClass aClass,
54     boolean includeSynthetic) {
55     Key<CachedValue<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>>> key =
56       includeSynthetic ? CACHED_MEMBERS_INCLUDING_SYNTHETIC : CACHED_MEMBERS;
57     CachedValue<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>> cachedValue = aClass.getUserData(key);
58     if (cachedValue == null) {
59       cachedValue = buildCache(aClass, includeSynthetic);
60       aClass.putUserData(key, cachedValue);
61     }
62     return cachedValue.getValue();
63   }
64
65   public static Map<String, CandidateInfo> getAllInnerClasses(final PsiClass aClass, boolean includeSynthetic) {
66     return getCachedMembers(aClass, includeSynthetic).getThird();
67   }
68
69   public static Map<String, CandidateInfo> getAllFields(final PsiClass aClass) {
70     return getCachedMembers(aClass, false).getFirst();
71   }
72
73   private static CachedValue<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>> buildCache(final PsiClass aClass, final boolean includeSynthetic) {
74     return CachedValuesManager.getManager(aClass.getProject()).createCachedValue(new CachedValueProvider<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>>() {
75       public Result<Trinity<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>, Map<String, CandidateInfo>>> compute() {
76         Map<String, CandidateInfo> allFields = new HashMap<String, CandidateInfo>();
77         Map<String, List<CandidateInfo>> allMethods = new HashMap<String, List<CandidateInfo>>();
78         Map<String, CandidateInfo> allInnerClasses = new HashMap<String, CandidateInfo>();
79
80         processClass(aClass, allFields, allMethods, allInnerClasses, new HashSet<PsiClass>(), PsiSubstitutor.EMPTY, includeSynthetic);
81         return Result.create(Trinity.create(allFields, allMethods, allInnerClasses), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
82       }
83     }, false);
84   }
85
86   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) {
87     if (visitedClasses.contains(aClass)) return;
88     visitedClasses.add(aClass);
89
90     for (PsiField field : aClass.getFields()) {
91       String name = field.getName();
92       if (!allFields.containsKey(name)) {
93         allFields.put(name, new CandidateInfo(field, substitutor));
94       } else if (hasExplicitVisibilityModifiers(field)) {
95         final CandidateInfo candidateInfo = allFields.get(name);
96         final PsiElement element = candidateInfo.getElement();
97         if (element instanceof GrField && (((GrField)element).getModifierList() == null ||
98                                            !(((GrField)element).getModifierList()).hasExplicitVisibilityModifiers()) &&
99             aClass == ((GrField)element).getContainingClass()) {
100           //replace property-field with field with explicit visibilityModifier
101           allFields.put(name, new CandidateInfo(field, substitutor));
102         }
103       }
104     }
105
106     for (PsiMethod method : includeSynthetic || !(aClass instanceof GrTypeDefinition) ? aClass.getMethods() : ((GrTypeDefinition) aClass).getGroovyMethods()) {
107       addMethod(allMethods, method, substitutor);
108     }
109
110     for (final PsiClass inner : aClass.getInnerClasses()) {
111       final String name = inner.getName();
112       if (name != null && !allInnerClasses.containsKey(name)) {
113         allInnerClasses.put(name, new CandidateInfo(inner, substitutor));
114       }
115     }
116
117     for (PsiClassType superType : aClass.getSuperTypes()) {
118       PsiClass superClass = superType.resolve();
119       if (superClass != null) {
120         final PsiSubstitutor superSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(superClass, aClass, substitutor);
121         processClass(superClass, allFields, allMethods, allInnerClasses, visitedClasses, superSubstitutor, includeSynthetic);
122       }
123     }
124   }
125
126   private static boolean hasExplicitVisibilityModifiers(PsiField field) {
127     if (field instanceof GrField) {
128       final GrModifierList list = (GrModifierList)field.getModifierList();
129       return list == null || list.hasExplicitVisibilityModifiers();
130     }
131     else {
132       return true;
133     }
134   }
135
136   private static void addMethod(Map<String, List<CandidateInfo>> allMethods, PsiMethod method, PsiSubstitutor substitutor) {
137     String name = method.getName();
138     List<CandidateInfo> methods = allMethods.get(name);
139     if (methods == null) {
140       methods = new ArrayList<CandidateInfo>();
141       allMethods.put(name, methods);
142       methods.add(new CandidateInfo(method, substitutor));
143     } else methods.add(new CandidateInfo(method, substitutor));
144   }
145 }