1 package com
.intellij
.psi
.scope
.conflictResolvers
;
3 import com
.intellij
.pom
.java
.LanguageLevel
;
4 import com
.intellij
.psi
.*;
5 import com
.intellij
.psi
.infos
.CandidateInfo
;
6 import com
.intellij
.psi
.infos
.MethodCandidateInfo
;
7 import com
.intellij
.psi
.scope
.PsiConflictResolver
;
8 import com
.intellij
.psi
.util
.*;
9 import com
.intellij
.util
.containers
.ContainerUtil
;
10 import com
.intellij
.util
.Function
;
11 import gnu
.trove
.THashSet
;
13 import java
.util
.HashMap
;
14 import java
.util
.Iterator
;
15 import java
.util
.List
;
19 * Created by IntelliJ IDEA.
23 * To change this template use Options | File Templates.
25 public class JavaMethodsConflictResolver
implements PsiConflictResolver
{
26 private final PsiElement myArgumentsList
;
27 private final PsiType
[] myActualParameterTypes
;
29 public JavaMethodsConflictResolver(PsiExpressionList list
){
30 myArgumentsList
= list
;
31 myActualParameterTypes
= ContainerUtil
.map2Array(list
.getExpressions(), PsiType
.class, new Function
<PsiExpression
, PsiType
>() {
32 public PsiType
fun(final PsiExpression expression
) {
33 return expression
.getType();
38 public JavaMethodsConflictResolver(final PsiElement argumentsList
, final PsiType
[] actualParameterTypes
) {
39 myArgumentsList
= argumentsList
;
40 myActualParameterTypes
= actualParameterTypes
;
43 public CandidateInfo
resolveConflict(List
<CandidateInfo
> conflicts
){
44 if (conflicts
.isEmpty()) return null;
45 if (conflicts
.size() == 1) return conflicts
.get(0);
46 checkSameSignatures(conflicts
);
48 if (conflicts
.size() == 1) return conflicts
.get(0);
49 checkAccessLevels(conflicts
);
51 if (conflicts
.size() == 1) return conflicts
.get(0);
53 checkParametersNumber(conflicts
, myActualParameterTypes
.length
);
54 if (conflicts
.size() == 1) return conflicts
.get(0);
56 final int applicabilityLevel
= checkApplicability(conflicts
);
57 checkSpecifics(conflicts
, applicabilityLevel
);
59 if (conflicts
.size() == 1) return conflicts
.get(0);
61 THashSet
<CandidateInfo
> uniques
= new THashSet
<CandidateInfo
>(conflicts
);
62 if (uniques
.size() == 1) return uniques
.iterator().next();
66 private void checkSpecifics(List
<CandidateInfo
> conflicts
, int applicabilityLevel
) {
67 final boolean applicable
= applicabilityLevel
> MethodCandidateInfo
.ApplicabilityLevel
.NOT_APPLICABLE
;
69 int conflictsCount
= conflicts
.size();
72 final CandidateInfo
[] newConflictsArray
= conflicts
.toArray(new CandidateInfo
[conflicts
.size()]);
73 for (int i
= 1; i
< conflictsCount
; i
++) {
74 final CandidateInfo method
= newConflictsArray
[i
];
75 for (int j
= 0; j
< i
; j
++) {
76 final CandidateInfo conflict
= newConflictsArray
[j
];
77 assert conflict
!= method
;
78 switch (isMoreSpecific(method
, conflict
, applicabilityLevel
)) {
80 conflicts
.remove(conflict
);
83 conflicts
.remove(method
);
93 private static void checkAccessLevels(List
<CandidateInfo
> conflicts
) {
94 int conflictsCount
= conflicts
.size();
96 int maxCheckLevel
= -1;
97 int[] checkLevels
= new int[conflictsCount
];
99 for (final CandidateInfo conflict
: conflicts
) {
100 final MethodCandidateInfo method
= (MethodCandidateInfo
)conflict
;
101 final int level
= getCheckLevel(method
);
102 checkLevels
[index
++] = level
;
103 maxCheckLevel
= Math
.max(maxCheckLevel
, level
);
106 for (int i
= conflictsCount
- 1; i
>= 0; i
--) {
108 if (checkLevels
[i
] < maxCheckLevel
) {
114 private static void checkSameSignatures(final List
<CandidateInfo
> conflicts
) {
115 // candidates should go in order of class hierarchy traversal
116 // in order for this to work
117 Map
<MethodSignature
, CandidateInfo
> signatures
= new HashMap
<MethodSignature
, CandidateInfo
>();
118 for (Iterator
<CandidateInfo
> iterator
= conflicts
.iterator(); iterator
.hasNext();) {
119 CandidateInfo info
= iterator
.next();
120 PsiMethod method
= (PsiMethod
)info
.getElement();
121 assert method
!= null;
122 PsiClass class1
= method
.getContainingClass();
123 MethodSignature signature
= method
.getSignature(info
.getSubstitutor());
124 CandidateInfo existing
= signatures
.get(signature
);
126 if (existing
== null) {
127 signatures
.put(signature
, info
);
130 PsiMethod existingMethod
= (PsiMethod
)existing
.getElement();
131 assert existingMethod
!= null;
132 PsiClass existingClass
= existingMethod
.getContainingClass();
133 if (class1
.isInterface() && "java.lang.Object".equals(existingClass
.getQualifiedName())) { //prefer interface methods to methods from Object
134 signatures
.put(signature
, info
);
137 if (method
== existingMethod
) {
138 PsiElement scope1
= info
.getCurrentFileResolveScope();
139 PsiElement scope2
= existing
.getCurrentFileResolveScope();
140 if (scope1
instanceof PsiClass
&& scope2
instanceof PsiClass
&& PsiTreeUtil
.isAncestor(scope1
, scope2
, true) && !existing
.isAccessible()) { //prefer methods from outer class to inaccessible base class methods
141 signatures
.put(signature
, info
);
145 PsiType returnType1
= method
.getReturnType();
146 PsiType returnType2
= existingMethod
.getReturnType();
147 if (returnType1
!= null && returnType2
!= null) {
148 returnType1
= info
.getSubstitutor().substitute(returnType1
);
149 returnType2
= existing
.getSubstitutor().substitute(returnType2
);
150 if (returnType1
.isAssignableFrom(returnType2
) && (InheritanceUtil
.isInheritorOrSelf(class1
, existingClass
, true) ||
151 InheritanceUtil
.isInheritorOrSelf(existingClass
, class1
, true))) {
158 private static void checkParametersNumber(final List
<CandidateInfo
> conflicts
, final int argumentsCount
) {
159 boolean parametersNumberMatch
= false;
160 for (CandidateInfo info
: conflicts
) {
161 if (info
instanceof MethodCandidateInfo
) {
162 final PsiMethod method
= ((MethodCandidateInfo
)info
).getElement();
163 if (method
.isVarArgs()) return;
164 if (method
.getParameterList().getParametersCount() == argumentsCount
) {
165 parametersNumberMatch
= true;
170 if (parametersNumberMatch
) {
171 for (Iterator
<CandidateInfo
> iterator
= conflicts
.iterator(); iterator
.hasNext();) {
172 CandidateInfo info
= iterator
.next();
173 if (info
instanceof MethodCandidateInfo
) {
174 final PsiMethod method
= ((MethodCandidateInfo
)info
).getElement();
175 if (method
.getParameterList().getParametersCount() != argumentsCount
) {
183 private static int checkApplicability(List
<CandidateInfo
> conflicts
) {
184 int maxApplicabilityLevel
= 0;
185 boolean toFilter
= false;
186 for (CandidateInfo conflict
: conflicts
) {
187 final int level
= ((MethodCandidateInfo
)conflict
).getApplicabilityLevel();
188 if (maxApplicabilityLevel
> 0 && maxApplicabilityLevel
!= level
) {
191 if (level
> maxApplicabilityLevel
) {
192 maxApplicabilityLevel
= level
;
197 for (Iterator
<CandidateInfo
> iterator
= conflicts
.iterator(); iterator
.hasNext();) {
198 CandidateInfo info
= iterator
.next();
199 final int level
= ((MethodCandidateInfo
)info
).getApplicabilityLevel(); //cached
200 if (level
< maxApplicabilityLevel
) {
206 return maxApplicabilityLevel
;
209 private static int getCheckLevel(MethodCandidateInfo method
){
210 boolean visible
= method
.isAccessible();// && !method.myStaticProblem;
211 boolean available
= method
.isStaticsScopeCorrect();
212 return (visible ?
1 : 0) << 2 |
213 (available ?
1 : 0) << 1 |
214 (method
.getCurrentFileResolveScope() instanceof PsiImportStaticStatement ?
0 : 1);
217 private enum Specifics
{
223 private static Specifics
checkSubtyping(PsiType type1
, PsiType type2
) {
224 final boolean assignable2From1
= TypeConversionUtil
.isAssignable(type2
, type1
, false);
225 final boolean assignable1From2
= TypeConversionUtil
.isAssignable(type1
, type2
, false);
226 if (assignable1From2
|| assignable2From1
) {
227 if (assignable1From2
&& assignable2From1
) {
231 return assignable1From2 ? Specifics
.FALSE
: Specifics
.TRUE
;
234 return Specifics
.CONFLICT
;
237 private boolean isBoxingHappened(PsiType argType
, PsiType parameterType
) {
238 if (argType
== null) return parameterType
instanceof PsiPrimitiveType
;
239 final LanguageLevel languageLevel
= PsiUtil
.getLanguageLevel(myArgumentsList
);
240 if (parameterType
instanceof PsiClassType
) {
241 parameterType
= ((PsiClassType
)parameterType
).setLanguageLevel(languageLevel
);
244 return TypeConversionUtil
.boxingConversionApplicable(parameterType
, argType
);
247 private Specifics
isMoreSpecific(final CandidateInfo info1
, final CandidateInfo info2
, final int applicabilityLevel
) {
248 PsiMethod method1
= (PsiMethod
)info1
.getElement();
249 PsiMethod method2
= (PsiMethod
)info2
.getElement();
250 final PsiClass class1
= method1
.getContainingClass();
251 final PsiClass class2
= method2
.getContainingClass();
253 final PsiParameter
[] params1
= method1
.getParameterList().getParameters();
254 final PsiParameter
[] params2
= method2
.getParameterList().getParameters();
256 final PsiTypeParameter
[] typeParameters1
= method1
.getTypeParameters();
257 final PsiTypeParameter
[] typeParameters2
= method2
.getTypeParameters();
258 final PsiSubstitutor classSubstitutor1
= info1
.getSubstitutor(); //substitutions for method type parameters will be ignored
259 final PsiSubstitutor classSubstitutor2
= info2
.getSubstitutor();
260 PsiSubstitutor methodSubstitutor1
= PsiSubstitutor
.EMPTY
;
261 PsiSubstitutor methodSubstitutor2
= PsiSubstitutor
.EMPTY
;
263 final int max
= Math
.max(params1
.length
, params2
.length
);
264 PsiType
[] types1
= new PsiType
[max
];
265 PsiType
[] types2
= new PsiType
[max
];
266 for (int i
= 0; i
< max
; i
++) {
267 PsiType type1
= params1
[Math
.min(i
, params1
.length
- 1)].getType();
268 PsiType type2
= params2
[Math
.min(i
, params2
.length
- 1)].getType();
269 if (applicabilityLevel
== MethodCandidateInfo
.ApplicabilityLevel
.VARARGS
) {
270 if (type1
instanceof PsiEllipsisType
&& type2
instanceof PsiEllipsisType
) {
271 type1
= ((PsiEllipsisType
)type1
).toArrayType();
272 type2
= ((PsiEllipsisType
)type2
).toArrayType();
275 type1
= type1
instanceof PsiEllipsisType ?
((PsiArrayType
)type1
).getComponentType() : type1
;
276 type2
= type2
instanceof PsiEllipsisType ?
((PsiArrayType
)type2
).getComponentType() : type2
;
284 if (typeParameters1
.length
== 0 || typeParameters2
.length
== 0) {
285 if (typeParameters1
.length
> 0) {
286 final PsiResolveHelper resolveHelper
= JavaPsiFacade
.getInstance(myArgumentsList
.getProject()).getResolveHelper();
287 methodSubstitutor1
= calculateMethodSubstitutor(typeParameters1
, types1
, types2
, resolveHelper
);
289 else if (typeParameters2
.length
> 0) {
290 final PsiResolveHelper resolveHelper
= JavaPsiFacade
.getInstance(myArgumentsList
.getProject()).getResolveHelper();
291 methodSubstitutor2
= calculateMethodSubstitutor(typeParameters2
, types2
, types1
, resolveHelper
);
295 PsiElementFactory factory
= JavaPsiFacade
.getInstance(myArgumentsList
.getProject()).getElementFactory();
296 methodSubstitutor1
= factory
.createRawSubstitutor(PsiSubstitutor
.EMPTY
, typeParameters1
);
297 methodSubstitutor2
= factory
.createRawSubstitutor(PsiSubstitutor
.EMPTY
, typeParameters2
);
300 int[] boxingHappened
= new int[2];
301 for (int i
= 0; i
< types1
.length
; i
++) {
302 PsiType type1
= classSubstitutor1
.substitute(methodSubstitutor1
.substitute(types1
[i
]));
303 PsiType type2
= classSubstitutor2
.substitute(methodSubstitutor2
.substitute(types2
[i
]));
304 PsiType argType
= i
< myActualParameterTypes
.length ? myActualParameterTypes
[i
] : null;
306 boxingHappened
[0] += isBoxingHappened(argType
, type1
) ?
1 : 0;
307 boxingHappened
[1] += isBoxingHappened(argType
, type2
) ?
1 : 0;
309 if (boxingHappened
[0] == 0 && boxingHappened
[1] > 0) return Specifics
.TRUE
;
310 if (boxingHappened
[0] > 0 && boxingHappened
[1] == 0) return Specifics
.FALSE
;
312 Specifics isMoreSpecific
= null;
313 for (int i
= 0; i
< types1
.length
; i
++) {
314 PsiType type1
= classSubstitutor1
.substitute(methodSubstitutor1
.substitute(types1
[i
]));
315 PsiType type2
= classSubstitutor2
.substitute(methodSubstitutor2
.substitute(types2
[i
]));
317 final Specifics specifics
= checkSubtyping(type1
, type2
);
318 if (specifics
== null) continue;
321 if (isMoreSpecific
== Specifics
.FALSE
) return Specifics
.CONFLICT
;
322 isMoreSpecific
= specifics
;
325 if (isMoreSpecific
== Specifics
.TRUE
) return Specifics
.CONFLICT
;
326 isMoreSpecific
= specifics
;
329 return Specifics
.CONFLICT
;
333 if (isMoreSpecific
== null && class1
!= class2
) {
334 if (class2
.isInheritor(class1
, true) || class1
.isInterface() && !class2
.isInterface()) {
335 if (MethodSignatureUtil
.isSubsignature(method1
.getSignature(info1
.getSubstitutor()), method2
.getSignature(info2
.getSubstitutor()))) {
336 isMoreSpecific
= Specifics
.FALSE
;
339 else if (class1
.isInheritor(class2
, true) || class2
.isInterface()) {
340 if (MethodSignatureUtil
.isSubsignature(method2
.getSignature(info2
.getSubstitutor()), method1
.getSignature(info1
.getSubstitutor()))) {
341 isMoreSpecific
= Specifics
.TRUE
;
345 if (isMoreSpecific
== null) {
346 if (typeParameters1
.length
< typeParameters2
.length
) return Specifics
.TRUE
;
347 if (typeParameters1
.length
> typeParameters2
.length
) return Specifics
.FALSE
;
348 return Specifics
.CONFLICT
;
351 return isMoreSpecific
;
354 private PsiSubstitutor
calculateMethodSubstitutor(final PsiTypeParameter
[] typeParameters
,
355 final PsiType
[] types1
,
356 final PsiType
[] types2
,
357 final PsiResolveHelper resolveHelper
) {
358 PsiSubstitutor substitutor
= resolveHelper
.inferTypeArguments(typeParameters
, types1
, types2
, PsiUtil
.getLanguageLevel(myArgumentsList
));
359 for (PsiTypeParameter typeParameter
: typeParameters
) {
360 if (!substitutor
.getSubstitutionMap().containsKey(typeParameter
)) {
361 substitutor
= substitutor
.put(typeParameter
, TypeConversionUtil
.typeParameterErasure(typeParameter
));