1 /* 2 * Copyright 2016 Federico Tomassetti 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 17 package com.github.javaparser.symbolsolver.resolution; 18 19 import com.github.javaparser.resolution.MethodAmbiguityException; 20 import com.github.javaparser.resolution.MethodUsage; 21 import com.github.javaparser.resolution.declarations.*; 22 import com.github.javaparser.resolution.types.*; 23 import com.github.javaparser.symbolsolver.core.resolution.Context; 24 import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserAnonymousClassDeclaration; 25 import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserClassDeclaration; 26 import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumDeclaration; 27 import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserInterfaceDeclaration; 28 import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserMethodDeclaration; 29 import com.github.javaparser.symbolsolver.javassistmodel.JavassistClassDeclaration; 30 import com.github.javaparser.symbolsolver.javassistmodel.JavassistEnumDeclaration; 31 import com.github.javaparser.symbolsolver.javassistmodel.JavassistInterfaceDeclaration; 32 import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; 33 import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; 34 import com.github.javaparser.symbolsolver.model.typesystem.*; 35 import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration; 36 import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionEnumDeclaration; 37 import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionInterfaceDeclaration; 38 39 import java.util.*; 40 import java.util.stream.Collectors; 41 42 /** 43 * @author Federico Tomassetti 44 */ 45 public class MethodResolutionLogic { 46 47 private static List<ResolvedType> groupVariadicParamValues(List<ResolvedType> argumentsTypes, int startVariadic, ResolvedType variadicType) { 48 List<ResolvedType> res = new ArrayList<>(argumentsTypes.subList(0, startVariadic)); 49 List<ResolvedType> variadicValues = argumentsTypes.subList(startVariadic, argumentsTypes.size()); 50 if (variadicValues.isEmpty()) { 51 // TODO if there are no variadic values we should default to the bound of the formal type 52 res.add(variadicType); 53 } else { 54 ResolvedType componentType = findCommonType(variadicValues); 55 res.add(new ResolvedArrayType(componentType)); 56 } 57 return res; 58 } 59 60 private static ResolvedType findCommonType(List<ResolvedType> variadicValues) { 61 if (variadicValues.isEmpty()) { 62 throw new IllegalArgumentException(); 63 } 64 // TODO implement this decently 65 return variadicValues.get(0); 66 } 67 68 public static boolean isApplicable(ResolvedMethodDeclaration method, String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) { 69 return isApplicable(method, name, argumentsTypes, typeSolver, false); 70 } 71 72 private static boolean isApplicable(ResolvedMethodDeclaration method, String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver, boolean withWildcardTolerance) { 73 if (!method.getName().equals(name)) { 74 return false; 75 } 76 if (method.hasVariadicParameter()) { 77 int pos = method.getNumberOfParams() - 1; 78 if (method.getNumberOfParams() == argumentsTypes.size()) { 79 // check if the last value is directly assignable as an array 80 ResolvedType expectedType = method.getLastParam().getType(); 81 ResolvedType actualType = argumentsTypes.get(pos); 82 if (!expectedType.isAssignableBy(actualType)) { 83 for (ResolvedTypeParameterDeclaration tp : method.getTypeParameters()) { 84 expectedType = replaceTypeParam(expectedType, tp, typeSolver); 85 } 86 if (!expectedType.isAssignableBy(actualType)) { 87 if (actualType.isArray() && expectedType.isAssignableBy(actualType.asArrayType().getComponentType())) { 88 argumentsTypes.set(pos, actualType.asArrayType().getComponentType()); 89 } else { 90 argumentsTypes = groupVariadicParamValues(argumentsTypes, pos, method.getLastParam().getType()); 91 } 92 } 93 } // else it is already assignable, nothing to do 94 } else { 95 if (pos > argumentsTypes.size()) { 96 return false; 97 } 98 argumentsTypes = groupVariadicParamValues(argumentsTypes, pos, method.getLastParam().getType()); 99 } 100 } 101 102 if (method.getNumberOfParams() != argumentsTypes.size()) { 103 return false; 104 } 105 Map<String, ResolvedType> matchedParameters = new HashMap<>(); 106 boolean needForWildCardTolerance = false; 107 for (int i = 0; i < method.getNumberOfParams(); i++) { 108 ResolvedType expectedType = method.getParam(i).getType(); 109 ResolvedType actualType = argumentsTypes.get(i); 110 if ((expectedType.isTypeVariable() && !(expectedType.isWildcard())) && expectedType.asTypeParameter().declaredOnMethod()) { 111 matchedParameters.put(expectedType.asTypeParameter().getName(), actualType); 112 continue; 113 } 114 boolean isAssignableWithoutSubstitution = expectedType.isAssignableBy(actualType) || 115 (method.getParam(i).isVariadic() && new ResolvedArrayType(expectedType).isAssignableBy(actualType)); 116 if (!isAssignableWithoutSubstitution && expectedType.isReferenceType() && actualType.isReferenceType()) { 117 isAssignableWithoutSubstitution = isAssignableMatchTypeParameters( 118 expectedType.asReferenceType(), 119 actualType.asReferenceType(), 120 matchedParameters); 121 } 122 if (!isAssignableWithoutSubstitution) { 123 List<ResolvedTypeParameterDeclaration> typeParameters = method.getTypeParameters(); 124 typeParameters.addAll(method.declaringType().getTypeParameters()); 125 for (ResolvedTypeParameterDeclaration tp : typeParameters) { 126 expectedType = replaceTypeParam(expectedType, tp, typeSolver); 127 } 128 129 if (!expectedType.isAssignableBy(actualType)) { 130 if (actualType.isWildcard() && withWildcardTolerance && !expectedType.isPrimitive()) { 131 needForWildCardTolerance = true; 132 continue; 133 } 134 if (method.hasVariadicParameter() && i == method.getNumberOfParams() - 1) { 135 if (new ResolvedArrayType(expectedType).isAssignableBy(actualType)) { 136 continue; 137 } 138 } 139 return false; 140 } 141 } 142 } 143 return !withWildcardTolerance || needForWildCardTolerance; 144 } 145 146 public static boolean isAssignableMatchTypeParameters(ResolvedType expected, ResolvedType actual, 147 Map<String, ResolvedType> matchedParameters) { 148 if (expected.isReferenceType() && actual.isReferenceType()) { 149 return isAssignableMatchTypeParameters(expected.asReferenceType(), actual.asReferenceType(), matchedParameters); 150 } else if (expected.isTypeVariable()) { 151 matchedParameters.put(expected.asTypeParameter().getName(), actual); 152 return true; 153 } else { 154 throw new UnsupportedOperationException(expected.getClass().getCanonicalName() + " " + actual.getClass().getCanonicalName()); 155 } 156 } 157 158 public static boolean isAssignableMatchTypeParameters(ResolvedReferenceType expected, ResolvedReferenceType actual, 159 Map<String, ResolvedType> matchedParameters) { 160 if (actual.getQualifiedName().equals(expected.getQualifiedName())) { 161 return isAssignableMatchTypeParametersMatchingQName(expected, actual, matchedParameters); 162 } else { 163 List<ResolvedReferenceType> ancestors = actual.getAllAncestors(); 164 for (ResolvedReferenceType ancestor : ancestors) { 165 if (isAssignableMatchTypeParametersMatchingQName(expected, ancestor, matchedParameters)) { 166 return true; 167 } 168 } 169 } 170 return false; 171 } 172 173 private static boolean isAssignableMatchTypeParametersMatchingQName(ResolvedReferenceType expected, ResolvedReferenceType actual, 174 Map<String, ResolvedType> matchedParameters) { 175 176 if (!expected.getQualifiedName().equals(actual.getQualifiedName())) { 177 return false; 178 } 179 if (expected.typeParametersValues().size() != actual.typeParametersValues().size()) { 180 throw new UnsupportedOperationException(); 181 //return true; 182 } 183 for (int i = 0; i < expected.typeParametersValues().size(); i++) { 184 ResolvedType expectedParam = expected.typeParametersValues().get(i); 185 ResolvedType actualParam = actual.typeParametersValues().get(i); 186 187 // In the case of nested parameterizations eg. List<R> <-> List<Integer> 188 // we should peel off one layer and ensure R <-> Integer 189 if (expectedParam.isReferenceType() && actualParam.isReferenceType()){ 190 ResolvedReferenceType r1 = expectedParam.asReferenceType(); 191 ResolvedReferenceType r2 = actualParam.asReferenceType(); 192 193 return isAssignableMatchTypeParametersMatchingQName(r1, r2, matchedParameters); 194 } 195 196 if (expectedParam.isTypeVariable()) { 197 String expectedParamName = expectedParam.asTypeParameter().getName(); 198 if (!actualParam.isTypeVariable() || !actualParam.asTypeParameter().getName().equals(expectedParamName)) { 199 return matchTypeVariable(expectedParam.asTypeVariable(), actualParam, matchedParameters); 200 } 201 } else if (expectedParam.isReferenceType()) { 202 if (actualParam.isTypeVariable()) { 203 return matchTypeVariable(actualParam.asTypeVariable(), expectedParam, matchedParameters); 204 } else if (!expectedParam.equals(actualParam)) { 205 return false; 206 } 207 } else if (expectedParam.isWildcard()) { 208 if (expectedParam.asWildcard().isExtends()) { 209 return isAssignableMatchTypeParameters(expectedParam.asWildcard().getBoundedType(), actual, matchedParameters); 210 } 211 // TODO verify super bound 212 return true; 213 } else { 214 throw new UnsupportedOperationException(expectedParam.describe()); 215 } 216 } 217 return true; 218 } 219 220 private static boolean matchTypeVariable(ResolvedTypeVariable typeVariable, ResolvedType type, Map<String, ResolvedType> matchedParameters) { 221 String typeParameterName = typeVariable.asTypeParameter().getName(); 222 if (matchedParameters.containsKey(typeParameterName)) { 223 ResolvedType matchedParameter = matchedParameters.get(typeParameterName); 224 if (matchedParameter.isAssignableBy(type)) { 225 return true; 226 } else if (type.isAssignableBy(matchedParameter)) { 227 // update matchedParameters to contain the more general type 228 matchedParameters.put(typeParameterName, type); 229 return true; 230 } 231 return false; 232 } else { 233 matchedParameters.put(typeParameterName, type); 234 } 235 return true; 236 } 237 238 public static ResolvedType replaceTypeParam(ResolvedType type, ResolvedTypeParameterDeclaration tp, TypeSolver typeSolver) { 239 if (type.isTypeVariable()) { 240 if (type.describe().equals(tp.getName())) { 241 List<ResolvedTypeParameterDeclaration.Bound> bounds = tp.getBounds(); 242 if (bounds.size() > 1) { 243 throw new UnsupportedOperationException(); 244 } else if (bounds.size() == 1) { 245 return bounds.get(0).getType(); 246 } else { 247 return new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver); 248 } 249 } 250 return type; 251 } else if (type.isPrimitive()) { 252 return type; 253 } else if (type.isArray()) { 254 return new ResolvedArrayType(replaceTypeParam(type.asArrayType().getComponentType(), tp, typeSolver)); 255 } else if (type.isReferenceType()) { 256 ResolvedReferenceType result = type.asReferenceType(); 257 result = result.transformTypeParameters(typeParam -> replaceTypeParam(typeParam, tp, typeSolver)).asReferenceType(); 258 return result; 259 } else if (type.isWildcard()) { 260 if (type.describe().equals(tp.getName())) { 261 List<ResolvedTypeParameterDeclaration.Bound> bounds = tp.getBounds(); 262 if (bounds.size() > 1) { 263 throw new UnsupportedOperationException(); 264 } else if (bounds.size() == 1) { 265 return bounds.get(0).getType(); 266 } else { 267 return new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver); 268 } 269 } 270 return type; 271 } else { 272 throw new UnsupportedOperationException("Replacing " + type + ", param " + tp + " with " + type.getClass().getCanonicalName()); 273 } 274 } 275 276 public static boolean isApplicable(MethodUsage method, String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) { 277 if (!method.getName().equals(name)) { 278 return false; 279 } 280 // TODO Consider varargs 281 if (method.getNoParams() != argumentsTypes.size()) { 282 return false; 283 } 284 for (int i = 0; i < method.getNoParams(); i++) { 285 ResolvedType expectedType = method.getParamType(i); 286 ResolvedType expectedTypeWithoutSubstitutions = expectedType; 287 ResolvedType expectedTypeWithInference = method.getParamType(i); 288 ResolvedType actualType = argumentsTypes.get(i); 289 290 List<ResolvedTypeParameterDeclaration> typeParameters = method.getDeclaration().getTypeParameters(); 291 typeParameters.addAll(method.declaringType().getTypeParameters()); 292 293 if (expectedType.describe().equals(actualType.describe())){ 294 return true; 295 } 296 297 Map<ResolvedTypeParameterDeclaration, ResolvedType> derivedValues = new HashMap<>(); 298 for (int j = 0; j < method.getParamTypes().size(); j++) { 299 ResolvedParameterDeclaration parameter = method.getDeclaration().getParam(i); 300 ResolvedType parameterType = parameter.getType(); 301 if (parameter.isVariadic()) { 302 parameterType = parameterType.asArrayType().getComponentType(); 303 } 304 inferTypes(argumentsTypes.get(j), parameterType, derivedValues); 305 } 306 307 for (Map.Entry<ResolvedTypeParameterDeclaration, ResolvedType> entry : derivedValues.entrySet()){ 308 ResolvedTypeParameterDeclaration tp = entry.getKey(); 309 expectedTypeWithInference = expectedTypeWithInference.replaceTypeVariables(tp, entry.getValue()); 310 } 311 312 for (ResolvedTypeParameterDeclaration tp : typeParameters) { 313 if (tp.getBounds().isEmpty()) { 314 //expectedType = expectedType.replaceTypeVariables(tp.getName(), new ReferenceTypeUsageImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver)); 315 expectedType = expectedType.replaceTypeVariables(tp, ResolvedWildcard.extendsBound(new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver))); 316 } else if (tp.getBounds().size() == 1) { 317 ResolvedTypeParameterDeclaration.Bound bound = tp.getBounds().get(0); 318 if (bound.isExtends()) { 319 //expectedType = expectedType.replaceTypeVariables(tp.getName(), bound.getType()); 320 expectedType = expectedType.replaceTypeVariables(tp, ResolvedWildcard.extendsBound(bound.getType())); 321 } else { 322 //expectedType = expectedType.replaceTypeVariables(tp.getName(), new ReferenceTypeUsageImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver)); 323 expectedType = expectedType.replaceTypeVariables(tp, ResolvedWildcard.superBound(bound.getType())); 324 } 325 } else { 326 throw new UnsupportedOperationException(); 327 } 328 } 329 ResolvedType expectedType2 = expectedTypeWithoutSubstitutions; 330 for (ResolvedTypeParameterDeclaration tp : typeParameters) { 331 if (tp.getBounds().isEmpty()) { 332 expectedType2 = expectedType2.replaceTypeVariables(tp, new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver)); 333 } else if (tp.getBounds().size() == 1) { 334 ResolvedTypeParameterDeclaration.Bound bound = tp.getBounds().get(0); 335 if (bound.isExtends()) { 336 expectedType2 = expectedType2.replaceTypeVariables(tp, bound.getType()); 337 } else { 338 expectedType2 = expectedType2.replaceTypeVariables(tp, new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver)); 339 } 340 } else { 341 throw new UnsupportedOperationException(); 342 } 343 } 344 if (!expectedType.isAssignableBy(actualType) 345 && !expectedType2.isAssignableBy(actualType) 346 && !expectedTypeWithInference.isAssignableBy(actualType) 347 && !expectedTypeWithoutSubstitutions.isAssignableBy(actualType)) { 348 return false; 349 } 350 } 351 return true; 352 } 353 354 private static List<ResolvedMethodDeclaration> getMethodsWithoutDuplicates(List<ResolvedMethodDeclaration> methods) { 355 Set<ResolvedMethodDeclaration> s = new TreeSet<>((m1, m2) -> { 356 if (m1 instanceof JavaParserMethodDeclaration && m2 instanceof JavaParserMethodDeclaration && 357 ((JavaParserMethodDeclaration) m1).getWrappedNode().equals(((JavaParserMethodDeclaration) m2).getWrappedNode())) { 358 return 0; 359 } 360 return 1; 361 }); 362 s.addAll(methods); 363 List<ResolvedMethodDeclaration> res = new ArrayList<>(); 364 Set<String> usedSignatures = new HashSet<>(); 365 for (ResolvedMethodDeclaration md : methods) { 366 String signature = md.getQualifiedSignature(); 367 if (!usedSignatures.contains(signature)) { 368 usedSignatures.add(signature); 369 res.add(md); 370 } 371 } 372 return res; 373 } 374 375 /** 376 * @param methods we expect the methods to be ordered such that inherited methods are later in the list 377 * @param name 378 * @param argumentsTypes 379 * @param typeSolver 380 * @return 381 */ 382 public static SymbolReference<ResolvedMethodDeclaration> findMostApplicable(List<ResolvedMethodDeclaration> methods, 383 String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) { 384 SymbolReference<ResolvedMethodDeclaration> res = findMostApplicable(methods, name, argumentsTypes, typeSolver, false); 385 if (res.isSolved()) { 386 return res; 387 } 388 return findMostApplicable(methods, name, argumentsTypes, typeSolver, true); 389 } 390 391 public static SymbolReference<ResolvedMethodDeclaration> findMostApplicable(List<ResolvedMethodDeclaration> methods, 392 String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver, boolean wildcardTolerance) { 393 List<ResolvedMethodDeclaration> applicableMethods = getMethodsWithoutDuplicates(methods).stream().filter((m) -> isApplicable(m, name, argumentsTypes, typeSolver, wildcardTolerance)).collect(Collectors.toList()); 394 if (applicableMethods.isEmpty()) { 395 return SymbolReference.unsolved(ResolvedMethodDeclaration.class); 396 } 397 398 if (applicableMethods.size() > 1) { 399 List<Integer> nullParamIndexes = new ArrayList<>(); 400 for (int i = 0; i < argumentsTypes.size(); i++) { 401 if (argumentsTypes.get(i).isNull()) { 402 nullParamIndexes.add(i); 403 } 404 } 405 if (!nullParamIndexes.isEmpty()) { 406 // remove method with array param if a non array exists and arg is null 407 Set<ResolvedMethodDeclaration> removeCandidates = new HashSet<>(); 408 for (Integer nullParamIndex: nullParamIndexes) { 409 for (ResolvedMethodDeclaration methDecl: applicableMethods) { 410 if (methDecl.getParam(nullParamIndex.intValue()).getType().isArray()) { 411 removeCandidates.add(methDecl); 412 } 413 } 414 } 415 if (!removeCandidates.isEmpty() && removeCandidates.size() < applicableMethods.size()) { 416 applicableMethods.removeAll(removeCandidates); 417 } 418 } 419 } 420 if (applicableMethods.size() == 1) { 421 return SymbolReference.solved(applicableMethods.get(0)); 422 } else { 423 ResolvedMethodDeclaration winningCandidate = applicableMethods.get(0); 424 ResolvedMethodDeclaration other = null; 425 boolean possibleAmbiguity = false; 426 for (int i = 1; i < applicableMethods.size(); i++) { 427 other = applicableMethods.get(i); 428 if (isMoreSpecific(winningCandidate, other, argumentsTypes, typeSolver)) { 429 possibleAmbiguity = false; 430 } else if (isMoreSpecific(other, winningCandidate, argumentsTypes, typeSolver)) { 431 possibleAmbiguity = false; 432 winningCandidate = other; 433 } else { 434 if (winningCandidate.declaringType().getQualifiedName().equals(other.declaringType().getQualifiedName())) { 435 possibleAmbiguity = true; 436 } else { 437 // we expect the methods to be ordered such that inherited methods are later in the list 438 } 439 } 440 } 441 if (possibleAmbiguity) { 442 // pick the first exact match if it exists 443 if (!isExactMatch(winningCandidate, argumentsTypes)) { 444 if (isExactMatch(other, argumentsTypes)) { 445 winningCandidate = other; 446 } else { 447 throw new MethodAmbiguityException("Ambiguous method call: cannot find a most applicable method: " + winningCandidate + ", " + other); 448 } 449 } 450 } 451 return SymbolReference.solved(winningCandidate); 452 } 453 } 454 455 protected static boolean isExactMatch(ResolvedMethodLikeDeclaration method, List<ResolvedType> argumentsTypes) { 456 for (int i = 0; i < method.getNumberOfParams(); i++) { 457 if (!method.getParam(i).getType().equals(argumentsTypes.get(i))) { 458 return false; 459 } 460 } 461 return true; 462 } 463 464 private static boolean isMoreSpecific(ResolvedMethodDeclaration methodA, ResolvedMethodDeclaration methodB, 465 List<ResolvedType> argumentTypes, TypeSolver typeSolver) { 466 boolean oneMoreSpecificFound = false; 467 if (methodA.getNumberOfParams() < methodB.getNumberOfParams()) { 468 return true; 469 } 470 if (methodA.getNumberOfParams() > methodB.getNumberOfParams()) { 471 return false; 472 } 473 for (int i = 0; i < methodA.getNumberOfParams(); i++) { 474 ResolvedType tdA = methodA.getParam(i).getType(); 475 ResolvedType tdB = methodB.getParam(i).getType(); 476 // B is more specific 477 if (tdB.isAssignableBy(tdA) && !tdA.isAssignableBy(tdB)) { 478 oneMoreSpecificFound = true; 479 } 480 // A is more specific 481 if (tdA.isAssignableBy(tdB) && !tdB.isAssignableBy(tdA)) { 482 return false; 483 } 484 } 485 486 if (!oneMoreSpecificFound) { 487 int lastIndex = argumentTypes.size() - 1; 488 489 if (methodA.hasVariadicParameter() && !methodB.hasVariadicParameter()) { 490 // if the last argument is an array then m1 is more specific 491 if (argumentTypes.get(lastIndex).isArray()) { 492 return true; 493 } 494 495 if (!argumentTypes.get(lastIndex).isArray()) { 496 return false; 497 } 498 } 499 if (!methodA.hasVariadicParameter() && methodB.hasVariadicParameter()) { 500 // if the last argument is an array and m1 is not variadic then 501 // it is not more specific 502 if (argumentTypes.get(lastIndex).isArray()) { 503 return false; 504 } 505 506 if (!argumentTypes.get(lastIndex).isArray()) { 507 return true; 508 } 509 } 510 } 511 512 return oneMoreSpecificFound; 513 } 514 515 private static boolean isMoreSpecific(MethodUsage methodA, MethodUsage methodB, TypeSolver typeSolver) { 516 boolean oneMoreSpecificFound = false; 517 for (int i = 0; i < methodA.getNoParams(); i++) { 518 ResolvedType tdA = methodA.getParamType(i); 519 ResolvedType tdB = methodB.getParamType(i); 520 521 boolean aIsAssignableByB = tdA.isAssignableBy(tdB); 522 boolean bIsAssignableByA = tdB.isAssignableBy(tdA); 523 524 // B is more specific 525 if (bIsAssignableByA && !aIsAssignableByB) { 526 oneMoreSpecificFound = true; 527 } 528 // A is more specific 529 if (aIsAssignableByB && !bIsAssignableByA) { 530 return false; 531 } 532 } 533 return oneMoreSpecificFound; 534 } 535 536 public static Optional<MethodUsage> findMostApplicableUsage(List<MethodUsage> methods, String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) { 537 List<MethodUsage> applicableMethods = methods.stream().filter((m) -> isApplicable(m, name, argumentsTypes, typeSolver)).collect(Collectors.toList()); 538 539 if (applicableMethods.isEmpty()) { 540 return Optional.empty(); 541 } 542 if (applicableMethods.size() == 1) { 543 return Optional.of(applicableMethods.get(0)); 544 } else { 545 MethodUsage winningCandidate = applicableMethods.get(0); 546 for (int i = 1; i < applicableMethods.size(); i++) { 547 MethodUsage other = applicableMethods.get(i); 548 if (isMoreSpecific(winningCandidate, other, typeSolver)) { 549 // nothing to do 550 } else if (isMoreSpecific(other, winningCandidate, typeSolver)) { 551 winningCandidate = other; 552 } else { 553 if (winningCandidate.declaringType().getQualifiedName().equals(other.declaringType().getQualifiedName())) { 554 if (!areOverride(winningCandidate, other)) { 555 throw new MethodAmbiguityException("Ambiguous method call: cannot find a most applicable method: " + winningCandidate + ", " + other + ". First declared in " + winningCandidate.declaringType().getQualifiedName()); 556 } 557 } else { 558 // we expect the methods to be ordered such that inherited methods are later in the list 559 //throw new UnsupportedOperationException(); 560 } 561 } 562 } 563 return Optional.of(winningCandidate); 564 } 565 } 566 567 private static boolean areOverride(MethodUsage winningCandidate, MethodUsage other) { 568 if (!winningCandidate.getName().equals(other.getName())) { 569 return false; 570 } 571 if (winningCandidate.getNoParams() != other.getNoParams()) { 572 return false; 573 } 574 for (int i = 0; i < winningCandidate.getNoParams(); i++) { 575 if (!winningCandidate.getParamTypes().get(i).equals(other.getParamTypes().get(i))) { 576 return false; 577 } 578 } 579 return true; 580 } 581 582 public static SymbolReference<ResolvedMethodDeclaration> solveMethodInType(ResolvedTypeDeclaration typeDeclaration, 583 String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) { 584 return solveMethodInType(typeDeclaration, name, argumentsTypes, false, typeSolver); 585 } 586 587 /** 588 * Replace TypeDeclaration.solveMethod 589 * 590 * @param typeDeclaration 591 * @param name 592 * @param argumentsTypes 593 * @param staticOnly 594 * @return 595 */ 596 public static SymbolReference<ResolvedMethodDeclaration> solveMethodInType(ResolvedTypeDeclaration typeDeclaration, 597 String name, List<ResolvedType> argumentsTypes, boolean staticOnly, 598 TypeSolver typeSolver) { 599 if (typeDeclaration instanceof JavaParserClassDeclaration) { 600 Context ctx = ((JavaParserClassDeclaration) typeDeclaration).getContext(); 601 return ctx.solveMethod(name, argumentsTypes, staticOnly, typeSolver); 602 } 603 if (typeDeclaration instanceof JavaParserInterfaceDeclaration) { 604 Context ctx = ((JavaParserInterfaceDeclaration) typeDeclaration).getContext(); 605 return ctx.solveMethod(name, argumentsTypes, staticOnly, typeSolver); 606 } 607 if (typeDeclaration instanceof JavaParserEnumDeclaration) { 608 if (name.equals("values") && argumentsTypes.isEmpty()) { 609 return SymbolReference.solved(new JavaParserEnumDeclaration.ValuesMethod((JavaParserEnumDeclaration) typeDeclaration, typeSolver)); 610 } 611 Context ctx = ((JavaParserEnumDeclaration) typeDeclaration).getContext(); 612 return ctx.solveMethod(name, argumentsTypes, staticOnly, typeSolver); 613 } 614 if (typeDeclaration instanceof JavaParserAnonymousClassDeclaration) { 615 Context ctx = ((JavaParserAnonymousClassDeclaration) typeDeclaration).getContext(); 616 return ctx.solveMethod(name, argumentsTypes, staticOnly, typeSolver); 617 } 618 if (typeDeclaration instanceof ReflectionClassDeclaration) { 619 return ((ReflectionClassDeclaration) typeDeclaration).solveMethod(name, argumentsTypes, staticOnly); 620 } 621 if (typeDeclaration instanceof ReflectionInterfaceDeclaration) { 622 return ((ReflectionInterfaceDeclaration) typeDeclaration).solveMethod(name, argumentsTypes, staticOnly); 623 } 624 if (typeDeclaration instanceof ReflectionEnumDeclaration) { 625 return ((ReflectionEnumDeclaration) typeDeclaration).solveMethod(name, argumentsTypes, staticOnly); 626 } 627 if (typeDeclaration instanceof JavassistInterfaceDeclaration) { 628 return ((JavassistInterfaceDeclaration) typeDeclaration).solveMethod(name, argumentsTypes, staticOnly); 629 } 630 if (typeDeclaration instanceof JavassistClassDeclaration) { 631 return ((JavassistClassDeclaration) typeDeclaration).solveMethod(name, argumentsTypes, staticOnly); 632 } 633 if (typeDeclaration instanceof JavassistEnumDeclaration) { 634 return ((JavassistEnumDeclaration) typeDeclaration).solveMethod(name, argumentsTypes, staticOnly); 635 } 636 throw new UnsupportedOperationException(typeDeclaration.getClass().getCanonicalName()); 637 } 638 639 private static void inferTypes(ResolvedType source, ResolvedType target, Map<ResolvedTypeParameterDeclaration, ResolvedType> mappings) { 640 641 642 if (source.equals(target)) { 643 return; 644 } 645 if (source.isReferenceType() && target.isReferenceType()) { 646 ResolvedReferenceType sourceRefType = source.asReferenceType(); 647 ResolvedReferenceType targetRefType = target.asReferenceType(); 648 if (sourceRefType.getQualifiedName().equals(targetRefType.getQualifiedName())) { 649 if (!sourceRefType.isRawType() && !targetRefType.isRawType()) { 650 for (int i = 0; i < sourceRefType.typeParametersValues().size(); i++) { 651 inferTypes(sourceRefType.typeParametersValues().get(i), targetRefType.typeParametersValues().get(i), mappings); 652 } 653 } 654 } 655 return; 656 } 657 if (source.isReferenceType() && target.isWildcard()) { 658 if (target.asWildcard().isBounded()) { 659 inferTypes(source, target.asWildcard().getBoundedType(), mappings); 660 return; 661 } 662 return; 663 } 664 if (source.isWildcard() && target.isWildcard()) { 665 return; 666 } 667 if (source.isReferenceType() && target.isTypeVariable()) { 668 mappings.put(target.asTypeParameter(), source); 669 return; 670 } 671 672 if (source.isWildcard() && target.isReferenceType()){ 673 if (source.asWildcard().isBounded()){ 674 inferTypes(source.asWildcard().getBoundedType(), target, mappings); 675 } 676 return; 677 } 678 679 if (source.isWildcard() && target.isTypeVariable()) { 680 mappings.put(target.asTypeParameter(), source); 681 return; 682 } 683 if (source.isTypeVariable() && target.isTypeVariable()) { 684 mappings.put(target.asTypeParameter(), source); 685 return; 686 } 687 if (source.isPrimitive() || target.isPrimitive()) { 688 return; 689 } 690 if (source.isNull()) { 691 return; 692 } 693 } 694 695 696 } 697