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
.util
.Key
;
19 import com
.intellij
.openapi
.util
.UserDataHolderEx
;
20 import com
.intellij
.psi
.*;
21 import com
.intellij
.psi
.impl
.source
.HierarchicalMethodSignatureImpl
;
22 import com
.intellij
.psi
.search
.searches
.DeepestSuperMethodsSearch
;
23 import com
.intellij
.psi
.search
.searches
.SuperMethodsSearch
;
24 import com
.intellij
.psi
.util
.*;
25 import com
.intellij
.util
.SmartList
;
26 import gnu
.trove
.THashMap
;
27 import gnu
.trove
.THashSet
;
28 import gnu
.trove
.TObjectHashingStrategy
;
29 import org
.jetbrains
.annotations
.NotNull
;
30 import org
.jetbrains
.annotations
.Nullable
;
34 public class PsiSuperMethodImplUtil
{
35 private static final Key
<CachedValue
<Map
<MethodSignature
, HierarchicalMethodSignature
>>> SIGNATURES_KEY
= Key
.create("MAP_KEY");
37 private PsiSuperMethodImplUtil() {
41 public static PsiMethod
[] findSuperMethods(PsiMethod method
) {
42 return findSuperMethods(method
, null);
46 public static PsiMethod
[] findSuperMethods(PsiMethod method
, boolean checkAccess
) {
47 if (!canHaveSuperMethod(method
, checkAccess
, false)) return PsiMethod
.EMPTY_ARRAY
;
48 return findSuperMethodsInternal(method
, null);
52 public static PsiMethod
[] findSuperMethods(PsiMethod method
, PsiClass parentClass
) {
53 if (!canHaveSuperMethod(method
, true, false)) return PsiMethod
.EMPTY_ARRAY
;
54 return findSuperMethodsInternal(method
, parentClass
);
59 private static PsiMethod
[] findSuperMethodsInternal(PsiMethod method
, PsiClass parentClass
) {
60 List
<MethodSignatureBackedByPsiMethod
> outputMethods
= findSuperMethodSignatures(method
, parentClass
, false);
62 return MethodSignatureUtil
.convertMethodSignaturesToMethods(outputMethods
);
66 public static List
<MethodSignatureBackedByPsiMethod
> findSuperMethodSignaturesIncludingStatic(PsiMethod method
,
67 boolean checkAccess
) {
68 if (!canHaveSuperMethod(method
, checkAccess
, true)) return Collections
.emptyList();
69 return findSuperMethodSignatures(method
, null, true);
73 private static List
<MethodSignatureBackedByPsiMethod
> findSuperMethodSignatures(PsiMethod method
,
75 boolean allowStaticMethod
) {
77 return new ArrayList
<MethodSignatureBackedByPsiMethod
>(SuperMethodsSearch
.search(method
, parentClass
, true, allowStaticMethod
).findAll());
80 private static boolean canHaveSuperMethod(PsiMethod method
, boolean checkAccess
, boolean allowStaticMethod
) {
81 if (method
.isConstructor()) return false;
82 if (!allowStaticMethod
&& method
.hasModifierProperty(PsiModifier
.STATIC
)) return false;
83 if (checkAccess
&& method
.hasModifierProperty(PsiModifier
.PRIVATE
)) return false;
84 PsiClass parentClass
= method
.getContainingClass();
85 return parentClass
!= null && !"java.lang.Object".equals(parentClass
.getQualifiedName());
89 public static PsiMethod
findDeepestSuperMethod(PsiMethod method
) {
90 if (!canHaveSuperMethod(method
, true, false)) return null;
91 return DeepestSuperMethodsSearch
.search(method
).findFirst();
94 public static PsiMethod
[] findDeepestSuperMethods(PsiMethod method
) {
95 if (!canHaveSuperMethod(method
, true, false)) return PsiMethod
.EMPTY_ARRAY
;
96 Collection
<PsiMethod
> collection
= DeepestSuperMethodsSearch
.search(method
).findAll();
97 return collection
.toArray(new PsiMethod
[collection
.size()]);
100 private static Map
<MethodSignature
, HierarchicalMethodSignature
> buildMethodHierarchy(PsiClass aClass
,
101 PsiSubstitutor substitutor
,
102 final boolean includePrivates
,
103 final Set
<PsiClass
> visited
,
104 boolean isInRawContext
) {
105 Map
<MethodSignature
, HierarchicalMethodSignature
> result
= new LinkedHashMap
<MethodSignature
, HierarchicalMethodSignature
>();
106 final Map
<MethodSignature
, List
<PsiMethod
>> sameParameterErasureMethods
= new THashMap
<MethodSignature
, List
<PsiMethod
>>(MethodSignatureUtil
.METHOD_PARAMETERS_ERASURE_EQUALITY
);
108 Map
<MethodSignature
, HierarchicalMethodSignatureImpl
> map
= new THashMap
<MethodSignature
, HierarchicalMethodSignatureImpl
>(new TObjectHashingStrategy
<MethodSignature
>() {
109 public int computeHashCode(MethodSignature signature
) {
110 return MethodSignatureUtil
.METHOD_PARAMETERS_ERASURE_EQUALITY
.computeHashCode(signature
);
113 public boolean equals(MethodSignature o1
, MethodSignature o2
) {
114 if (!MethodSignatureUtil
.METHOD_PARAMETERS_ERASURE_EQUALITY
.equals(o1
, o2
)) return false;
115 List
<PsiMethod
> list
= sameParameterErasureMethods
.get(o1
);
116 boolean toCheckReturnType
= list
!= null && list
.size() > 1;
117 if (!toCheckReturnType
) return true;
118 PsiType returnType1
= ((MethodSignatureBackedByPsiMethod
)o1
).getMethod().getReturnType();
119 PsiType returnType2
= ((MethodSignatureBackedByPsiMethod
)o2
).getMethod().getReturnType();
120 if (returnType1
== null && returnType2
== null) return true;
121 if (returnType1
== null || returnType2
== null) return false;
123 PsiType erasure1
= TypeConversionUtil
.erasure(o1
.getSubstitutor().substitute(returnType1
));
124 PsiType erasure2
= TypeConversionUtil
.erasure(o2
.getSubstitutor().substitute(returnType2
));
125 return erasure1
.equals(erasure2
);
129 for (PsiMethod method
: aClass
.getMethods()) {
130 if (!includePrivates
&& method
.hasModifierProperty(PsiModifier
.PRIVATE
)) continue;
131 final MethodSignatureBackedByPsiMethod signature
= MethodSignatureBackedByPsiMethod
.create(method
, substitutor
, isInRawContext
);
132 HierarchicalMethodSignatureImpl newH
= new HierarchicalMethodSignatureImpl(signature
);
134 List
<PsiMethod
> list
= sameParameterErasureMethods
.get(signature
);
136 list
= new SmartList
<PsiMethod
>();
137 sameParameterErasureMethods
.put(signature
, list
);
141 result
.put(signature
, newH
);
142 map
.put(signature
, newH
);
145 for (PsiClassType superType
: aClass
.getSuperTypes()) {
146 PsiClassType
.ClassResolveResult superTypeResolveResult
= superType
.resolveGenerics();
147 PsiClass superClass
= superTypeResolveResult
.getElement();
148 if (superClass
== null) continue;
149 if (!visited
.add(superClass
)) continue; // cyclic inheritance
150 final PsiSubstitutor superSubstitutor
= superTypeResolveResult
.getSubstitutor();
151 PsiSubstitutor finalSubstitutor
= obtainFinalSubstitutor(superClass
, superSubstitutor
, substitutor
);
153 final boolean isInRawContextSuper
= (isInRawContext
|| PsiUtil
.isRawSubstitutor(superClass
, superSubstitutor
)) && superClass
.getTypeParameters().length
!= 0;
154 Map
<MethodSignature
, HierarchicalMethodSignature
> superResult
= buildMethodHierarchy(superClass
, finalSubstitutor
, false, visited
, isInRawContextSuper
);
155 visited
.remove(superClass
);
157 for (Map
.Entry
<MethodSignature
, HierarchicalMethodSignature
> entry
: superResult
.entrySet()) {
158 HierarchicalMethodSignature hierarchicalMethodSignature
= entry
.getValue();
159 if (!PsiUtil
.isAccessible(hierarchicalMethodSignature
.getMethod(), aClass
, aClass
)) continue;
160 MethodSignature superSignature
= entry
.getKey();
161 HierarchicalMethodSignatureImpl existing
= map
.get(superSignature
);
162 if (existing
== null) {
163 map
.put(superSignature
, copy(hierarchicalMethodSignature
));
165 else if (isReturnTypeIsMoreSpecificThan(hierarchicalMethodSignature
, existing
) && isSuperMethod(aClass
, hierarchicalMethodSignature
, existing
)) {
166 HierarchicalMethodSignatureImpl newSuper
= copy(hierarchicalMethodSignature
);
167 mergeSupers(newSuper
, existing
);
168 map
.put(superSignature
, newSuper
);
170 else if (isSuperMethod(aClass
, existing
, hierarchicalMethodSignature
)) {
171 mergeSupers(existing
, hierarchicalMethodSignature
);
173 // just drop an invalid method declaration there - to highlight accordingly
174 else if (!result
.containsKey(superSignature
)) {
175 result
.put(superSignature
, hierarchicalMethodSignature
);
181 for (Map
.Entry
<MethodSignature
, HierarchicalMethodSignatureImpl
> entry
: map
.entrySet()) {
182 HierarchicalMethodSignatureImpl hierarchicalMethodSignature
= entry
.getValue();
183 MethodSignature methodSignature
= entry
.getKey();
184 if (result
.get(methodSignature
) == null && PsiUtil
.isAccessible(hierarchicalMethodSignature
.getMethod(), aClass
, aClass
)) {
185 result
.put(methodSignature
, hierarchicalMethodSignature
);
192 private static boolean isReturnTypeIsMoreSpecificThan(@NotNull HierarchicalMethodSignature thisSig
, @NotNull HierarchicalMethodSignature thatSig
) {
193 PsiType thisRet
= thisSig
.getMethod().getReturnType();
194 PsiType thatRet
= thatSig
.getMethod().getReturnType();
195 return thatRet
!= null && thisRet
!= null && !thatRet
.equals(thisRet
) && TypeConversionUtil
.isAssignable(thatRet
, thisRet
);
198 private static void mergeSupers(final HierarchicalMethodSignatureImpl existing
, final HierarchicalMethodSignature superSignature
) {
199 for (HierarchicalMethodSignature existingSuper
: existing
.getSuperSignatures()) {
200 if (existingSuper
.getMethod() == superSignature
.getMethod()) {
201 for (HierarchicalMethodSignature signature
: superSignature
.getSuperSignatures()) {
202 mergeSupers((HierarchicalMethodSignatureImpl
)existingSuper
, signature
);
207 if (existing
.getMethod() == superSignature
.getMethod()) {
208 List
<HierarchicalMethodSignature
> existingSupers
= existing
.getSuperSignatures();
209 for (HierarchicalMethodSignature supers
: superSignature
.getSuperSignatures()) {
210 if (!existingSupers
.contains(supers
)) existing
.addSuperSignature(copy(supers
));
214 HierarchicalMethodSignatureImpl copy
= copy(superSignature
);
215 existing
.addSuperSignature(copy
);
219 private static boolean isSuperMethod(PsiClass aClass
,
220 HierarchicalMethodSignature hierarchicalMethodSignature
,
221 HierarchicalMethodSignature superSignatureHierarchical
) {
222 PsiMethod superMethod
= superSignatureHierarchical
.getMethod();
223 PsiClass superClass
= superMethod
.getContainingClass();
224 PsiClass containingClass
= hierarchicalMethodSignature
.getMethod().getContainingClass();
225 return !superMethod
.isConstructor()
226 && !aClass
.equals(superClass
)
227 && PsiUtil
.isAccessible(superMethod
, aClass
, aClass
)
228 && MethodSignatureUtil
.isSubsignature(superSignatureHierarchical
, hierarchicalMethodSignature
)
229 && superClass
!= null
230 && (containingClass
!= null && containingClass
.isInterface() == superClass
.isInterface() || superClass
.isInterface() || "java.lang.Object".equals(superClass
.getQualifiedName()))
234 private static HierarchicalMethodSignatureImpl
copy(HierarchicalMethodSignature hi
) {
235 HierarchicalMethodSignatureImpl hierarchicalMethodSignature
= new HierarchicalMethodSignatureImpl(hi
);
236 for (HierarchicalMethodSignature his
: hi
.getSuperSignatures()) {
237 hierarchicalMethodSignature
.addSuperSignature(copy(his
));
239 return hierarchicalMethodSignature
;
242 private static PsiSubstitutor
obtainFinalSubstitutor(PsiClass superClass
,
243 PsiSubstitutor superSubstitutor
,
244 PsiSubstitutor derivedSubstitutor
) {
245 Map
<PsiTypeParameter
, PsiType
> map
= null;
246 for (PsiTypeParameter typeParameter
: PsiUtil
.typeParametersIterable(superClass
)) {
247 PsiType type
= superSubstitutor
.substitute(typeParameter
);
248 final PsiType t
= derivedSubstitutor
.substitute(type
);
250 map
= new THashMap
<PsiTypeParameter
, PsiType
>();
252 map
.put(typeParameter
, t
);
255 return map
== null ? PsiSubstitutor
.EMPTY
: JavaPsiFacade
.getInstance(superClass
.getProject()).getElementFactory().createSubstitutor(map
);
258 public static Collection
<HierarchicalMethodSignature
> getVisibleSignatures(PsiClass aClass
) {
259 Map
<MethodSignature
, HierarchicalMethodSignature
> map
= getSignaturesMap(aClass
);
263 @NotNull public static HierarchicalMethodSignature
getHierarchicalMethodSignature(PsiMethod method
) {
264 PsiClass aClass
= method
.getContainingClass();
265 HierarchicalMethodSignature result
= null;
266 if (aClass
!= null) {
267 result
= getSignaturesMap(aClass
).get(method
.getSignature(PsiSubstitutor
.EMPTY
));
269 if (result
== null) {
270 result
= new HierarchicalMethodSignatureImpl((MethodSignatureBackedByPsiMethod
)method
.getSignature(PsiSubstitutor
.EMPTY
));
275 private static Map
<MethodSignature
, HierarchicalMethodSignature
> getSignaturesMap(final PsiClass aClass
) {
276 CachedValue
<Map
<MethodSignature
, HierarchicalMethodSignature
>> value
= aClass
.getUserData(SIGNATURES_KEY
);
278 BySignaturesCachedValueProvider provider
= new BySignaturesCachedValueProvider(aClass
);
279 value
= CachedValuesManager
.getManager(aClass
.getProject()).createCachedValue(provider
, false);
280 //Do not cache for nonphysical elements
281 if (aClass
.isPhysical()) {
282 UserDataHolderEx dataHolder
= (UserDataHolderEx
)aClass
;
283 value
= dataHolder
.putUserDataIfAbsent(SIGNATURES_KEY
, value
);
287 return value
.getValue();
290 private static class BySignaturesCachedValueProvider
implements CachedValueProvider
<Map
<MethodSignature
, HierarchicalMethodSignature
>> {
291 private final PsiClass myClass
;
293 private BySignaturesCachedValueProvider(final PsiClass aClass
) {
297 public Result
<Map
<MethodSignature
, HierarchicalMethodSignature
>> compute() {
298 Map
<MethodSignature
, HierarchicalMethodSignature
> result
= buildMethodHierarchy(myClass
, PsiSubstitutor
.EMPTY
, true, new THashSet
<PsiClass
>(), false);
299 assert result
!= null;
302 return new Result
<Map
<MethodSignature
, HierarchicalMethodSignature
>>(result
, PsiModificationTracker
.JAVA_STRUCTURE_MODIFICATION_COUNT
);