1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.jexl3.internal.introspection; 18 19 import java.lang.reflect.Constructor; 20 import java.lang.reflect.Executable; 21 import java.lang.reflect.Method; 22 import java.util.Arrays; 23 import java.util.Deque; 24 import java.util.HashMap; 25 import java.util.Iterator; 26 import java.util.LinkedList; 27 import java.util.Map; 28 29 /** 30 * A method key usable by the introspector cache. 31 * <p> 32 * This stores a method (or class) name and parameters. 33 * </p> 34 * <p> 35 * This replaces the original key scheme which used to build the key 36 * by concatenating the method name and parameters class names as one string 37 * with the exception that primitive types were converted to their object class equivalents. 38 * </p> 39 * <p> 40 * The key is still based on the same information, it is just wrapped in an object instead. 41 * Primitive type classes are converted to they object equivalent to make a key; 42 * int foo(int) and int foo(Integer) do generate the same key. 43 * </p> 44 * A key can be constructed either from arguments (array of objects) or from parameters 45 * (array of class). 46 * Roughly 3x faster than string key to access the map and uses less memory. 47 */ 48 public final class MethodKey { 49 /** 50 * Simple distinguishable exception, used when 51 * we run across ambiguous overloading. Caught 52 * by the introspector. 53 */ 54 public static class AmbiguousException extends RuntimeException { 55 /** Version identifier for serializable. */ 56 private static final long serialVersionUID = -201801091655L; 57 /** Whether this exception should be considered severe. */ 58 private final boolean severe; 59 60 /** 61 * A severe or not ambiguous exception. 62 * @param flag logging flag 63 */ 64 AmbiguousException(final boolean flag) { 65 this.severe = flag; 66 } 67 68 /** 69 * Whether this exception is considered severe or benign. 70 * <p>Note that this is meant in the context of an ambiguous exception; benign cases can only be triggered 71 * by null arguments often related to runtime problems (not simply on overload signatures). 72 * @return true if severe, false if benign. 73 */ 74 public boolean isSevere() { 75 return severe; 76 } 77 } 78 /** The initial size of the primitive conversion map. */ 79 private static final int PRIMITIVE_SIZE = 11; 80 /** A marker for empty parameter list. */ 81 private static final Class<?>[] NOARGS = {}; 82 /** The hash code constants. */ 83 private static final int HASH = 37; 84 /** 85 * Maps from primitive types to invocation compatible classes. 86 * <p>Considering the key as a parameter type, the value is the list of argument classes that are invocation 87 * compatible with the parameter. Example is Long is invocation convertible to long. 88 */ 89 private static final Map<Class<?>, Class<?>[]> CONVERTIBLES; 90 static { 91 CONVERTIBLES = new HashMap<>(PRIMITIVE_SIZE); 92 CONVERTIBLES.put(Boolean.TYPE, 93 asArray(Boolean.class)); 94 CONVERTIBLES.put(Character.TYPE, 95 asArray(Character.class)); 96 CONVERTIBLES.put(Byte.TYPE, 97 asArray(Byte.class)); 98 CONVERTIBLES.put(Short.TYPE, 99 asArray(Short.class, Byte.class)); 100 CONVERTIBLES.put(Integer.TYPE, 101 asArray(Integer.class, Short.class, Byte.class)); 102 CONVERTIBLES.put(Long.TYPE, 103 asArray(Long.class, Integer.class, Short.class, Byte.class)); 104 CONVERTIBLES.put(Float.TYPE, 105 asArray(Float.class, Long.class, Integer.class, Short.class, Byte.class)); 106 CONVERTIBLES.put(Double.TYPE, 107 asArray(Double.class, Float.class, Long.class, Integer.class, Short.class, Byte.class)); 108 } 109 110 /** 111 * Maps from primitive types to invocation compatible primitive types. 112 * <p>Considering the key as a parameter type, the value is the list of argument types that are invocation 113 * compatible with the parameter. Example is 'int' is invocation convertible to 'long'. 114 */ 115 private static final Map<Class<?>, Class<?>[]> STRICT_CONVERTIBLES; 116 117 static { 118 STRICT_CONVERTIBLES = new HashMap<>(PRIMITIVE_SIZE); 119 STRICT_CONVERTIBLES.put(Short.TYPE, 120 asArray(Byte.TYPE)); 121 STRICT_CONVERTIBLES.put(Integer.TYPE, 122 asArray(Short.TYPE, Byte.TYPE)); 123 STRICT_CONVERTIBLES.put(Long.TYPE, 124 asArray(Integer.TYPE, Short.TYPE, Byte.TYPE)); 125 STRICT_CONVERTIBLES.put(Float.TYPE, 126 asArray(Long.TYPE, Integer.TYPE, Short.TYPE, Byte.TYPE)); 127 STRICT_CONVERTIBLES.put(Double.TYPE, 128 asArray(Float.TYPE, Long.TYPE, Integer.TYPE, Short.TYPE, Byte.TYPE)); 129 } 130 131 /** 132 * whether a method/ctor is more specific than a previously compared one. 133 */ 134 private static final int MORE_SPECIFIC = 0; 135 136 /** 137 * whether a method/ctor is less specific than a previously compared one. 138 */ 139 private static final int LESS_SPECIFIC = 1; 140 141 /** 142 * A method/ctor doesn't match a previously compared one. 143 */ 144 private static final int INCOMPARABLE = 2; 145 146 /** 147 * Creates an ambiguous exception. 148 * <p> 149 * This method computes the severity of the ambiguity. The only <em>non-severe</em> case is when there is 150 * at least one null argument and at most one applicable method or constructor has a corresponding 'Object' 151 * parameter. 152 * We thus consider that ambiguity is benign in presence of null arguments but severe in the case where 153 * the corresponding parameter is of type Object in more than one applicable overloads. 154 * <p> 155 * Rephrasing: 156 * <ul> 157 * <li>If all arguments are valid instances - no null argument -, ambiguity is severe.</li> 158 * <li>If there is at least one null argument, the ambiguity is severe if more than one method has a 159 * corresponding parameter of class 'Object'.</li> 160 * </ul> 161 * 162 * @param classes the argument args 163 * @param applicables the list of applicable methods or constructors 164 * @return an ambiguous exception 165 */ 166 private static <T extends Executable> 167 AmbiguousException ambiguousException(final Class<?>[] classes, final Iterable<T> applicables) { 168 boolean severe = false; 169 int instanceArgCount = 0; // count the number of valid instances, aka not null 170 for (int c = 0; c < classes.length; ++c) { 171 final Class<?> argClazz = classes[c]; 172 if (Void.class.equals(argClazz)) { 173 // count the number of methods for which the current arg maps to an Object parameter 174 int objectParmCount = 0; 175 for (final T app : applicables) { 176 final Class<?>[] parmClasses = app.getParameterTypes(); 177 final Class<?> parmClass = parmClasses[c]; 178 if (Object.class.equals(parmClass) && objectParmCount++ == 2) { 179 severe = true; 180 break; 181 } 182 } 183 } else { 184 instanceArgCount += 1; 185 } 186 } 187 return new AmbiguousException(severe || instanceArgCount == classes.length); 188 } 189 190 /** 191 * Helper to build class arrays. 192 * @param args the classes 193 * @return the array 194 */ 195 private static Class<?>[] asArray(final Class<?>... args) { 196 return args; 197 } 198 199 /** 200 * Returns all methods that are applicable to actual argument types. 201 * 202 * @param methods list of all candidate methods 203 * @param classes the actual types of the arguments 204 * @return a list that contains only applicable methods (number of 205 * formal and actual arguments matches, and argument types are assignable 206 * to formal types through a method invocation conversion). 207 */ 208 private static <T extends Executable> Deque<T> getApplicables(final T[] methods, final Class<?>[] classes) { 209 final Deque<T> list = new LinkedList<>(); 210 for (final T method : methods) { 211 if (isApplicable(method, classes)) { 212 list.add(method); 213 } 214 } 215 return list; 216 } 217 218 /** 219 * Returns true if the supplied method is applicable to actual 220 * argument types. 221 * 222 * @param method method that will be called 223 * @param actuals arguments signature for method 224 * @return true if method is applicable to arguments 225 */ 226 private static <T extends Executable> boolean isApplicable(final T method, final Class<?>[] actuals) { 227 final Class<?>[] formals = method.getParameterTypes(); 228 // if same number or args or 229 // there's just one more methodArg than class arg 230 // and the last methodArg is an array, then treat it as a vararg 231 if (formals.length == actuals.length) { 232 // this will properly match when the last methodArg 233 // is an array/varargs and the last class is the type of array 234 // (e.g. String when the method is expecting String...) 235 for (int i = 0; i < actuals.length; ++i) { 236 if (!isConvertible(formals[i], actuals[i], false)) { 237 // if we're on the last arg and the method expects an array 238 if (i == actuals.length - 1 && formals[i].isArray()) { 239 // check to see if the last arg is convertible 240 // to the array's component type 241 return isConvertible(formals[i], actuals[i], true); 242 } 243 return false; 244 } 245 } 246 return true; 247 } 248 249 // number of formal and actual differ, method must be vararg 250 if (!MethodKey.isVarArgs(method)) { 251 return false; 252 } 253 254 // fewer arguments than method parameters: vararg is null 255 if (formals.length > actuals.length) { 256 // only one parameter, the last (ie vararg) can be missing 257 if (formals.length - actuals.length > 1) { 258 return false; 259 } 260 // check that all present args match up to the method parms 261 for (int i = 0; i < actuals.length; ++i) { 262 if (!isConvertible(formals[i], actuals[i], false)) { 263 return false; 264 } 265 } 266 return true; 267 } 268 269 // more arguments given than the method accepts; check for varargs 270 if (formals.length > 0) { 271 // check that they all match up to the last method arg 272 for (int i = 0; i < formals.length - 1; ++i) { 273 if (!isConvertible(formals[i], actuals[i], false)) { 274 return false; 275 } 276 } 277 // check that all remaining arguments are convertible to the vararg type 278 // (last parm is an array since method is vararg) 279 final Class<?> vararg = formals[formals.length - 1].getComponentType(); 280 for (int i = formals.length - 1; i < actuals.length; ++i) { 281 if (!isConvertible(vararg, actuals[i], false)) { 282 return false; 283 } 284 } 285 return true; 286 } 287 // no match 288 return false; 289 } 290 291 /** 292 * @param formal the formal parameter type to which the actual 293 * parameter type should be convertible 294 * @param actual the actual parameter type. 295 * @param possibleVarArg whether we're dealing with the last parameter 296 * in the method declaration 297 * @return see isMethodInvocationConvertible. 298 * @see #isInvocationConvertible(Class, Class, boolean) 299 */ 300 private static boolean isConvertible(final Class<?> formal, final Class<?> actual, final boolean possibleVarArg) { 301 // if we see Void.class, the argument was null 302 return isInvocationConvertible(formal, actual.equals(Void.class) ? null : actual, possibleVarArg); 303 } 304 305 /** 306 * Determines whether a type represented by a class object is 307 * convertible to another type represented by a class object using a 308 * method invocation conversion, treating object types of primitive 309 * types as if they were primitive types (that is, a Boolean actual 310 * parameter type matches boolean primitive formal type). This behavior 311 * is because this method is used to determine applicable methods for 312 * an actual parameter list, and primitive types are represented by 313 * their object duals in reflective method calls. 314 * 315 * @param formal the formal parameter type to which the actual 316 * parameter type should be convertible 317 * @param actual the actual parameter type. 318 * @param possibleVarArg whether we're dealing with the last parameter 319 * in the method declaration 320 * @return true if either formal type is assignable from actual type, 321 * or formal is a primitive type and actual is its corresponding object 322 * type or an object-type of a primitive type that can be converted to 323 * the formal type. 324 */ 325 public static boolean isInvocationConvertible(final Class<?> formal, 326 final Class<?> actual, 327 final boolean possibleVarArg) { 328 return isInvocationConvertible(formal, actual, false, possibleVarArg); 329 } 330 331 /** 332 * Determines parameter-argument invocation compatibility. 333 * 334 * @param formal the formal parameter type 335 * @param type the argument type 336 * @param strict whether the check is strict or not 337 * @param possibleVarArg whether we're dealing with the last parameter in the method declaration 338 * @return true if compatible, false otherwise 339 */ 340 private static boolean isInvocationConvertible( 341 final Class<?> formal, final Class<?> type, final boolean strict, final boolean possibleVarArg) { 342 Class<?> actual = type; 343 /* if it is a null, it means the arg was null */ 344 if (actual == null && !formal.isPrimitive()) { 345 return true; 346 } 347 /* system asssignable, both sides must be arrays or not */ 348 if (actual != null && formal.isAssignableFrom(actual) && actual.isArray() == formal.isArray()) { 349 return true; 350 } 351 /* catch all... */ 352 if (!strict && formal == Object.class) { 353 return true; 354 } 355 /* Primitive conversion check. */ 356 if (formal.isPrimitive()) { 357 final Class<?>[] clist = strict ? STRICT_CONVERTIBLES.get(formal) : CONVERTIBLES.get(formal); 358 if (clist != null) { 359 for (final Class<?> aClass : clist) { 360 if (actual == aClass) { 361 return true; 362 } 363 } 364 } 365 return false; 366 } 367 /* Check for vararg conversion. */ 368 if (possibleVarArg && formal.isArray()) { 369 if (actual.isArray()) { 370 actual = actual.getComponentType(); 371 } 372 return isInvocationConvertible(formal.getComponentType(), actual, strict, false); 373 } 374 return false; 375 } 376 377 /** 378 * Checks whether a parameter class is a primitive. 379 * 380 * @param c the parameter class 381 * @param possibleVarArg true if this is the last parameter which can be a primitive array (vararg call) 382 * @return true if primitive, false otherwise 383 */ 384 private static boolean isPrimitive(final Class<?> c, final boolean possibleVarArg) { 385 if (c != null) { 386 if (c.isPrimitive()) { 387 return true; 388 } 389 if (possibleVarArg) { 390 final Class<?> t = c.getComponentType(); 391 return t != null && t.isPrimitive(); 392 } 393 } 394 return false; 395 } 396 397 /** 398 * @param formal the formal parameter type to which the actual 399 * parameter type should be convertible 400 * @param actual the actual parameter type. 401 * @param possibleVarArg whether we're dealing with the last parameter 402 * in the method declaration 403 * @return see isStrictMethodInvocationConvertible. 404 * @see #isStrictInvocationConvertible(Class, Class, boolean) 405 */ 406 private static boolean isStrictConvertible(final Class<?> formal, final Class<?> actual, 407 final boolean possibleVarArg) { 408 // if we see Void.class, the argument was null 409 return isStrictInvocationConvertible(formal, actual.equals(Void.class) ? null : actual, possibleVarArg); 410 } 411 412 /** 413 * Determines whether a type represented by a class object is 414 * convertible to another type represented by a class object using a 415 * method invocation conversion, without matching object and primitive 416 * types. This method is used to determine the more specific type when 417 * comparing signatures of methods. 418 * 419 * @param formal the formal parameter type to which the actual 420 * parameter type should be convertible 421 * @param actual the actual parameter type. 422 * @param possibleVarArg whether not we're dealing with the last parameter 423 * in the method declaration 424 * @return true if either formal type is assignable from actual type, 425 * or formal and actual are both primitive types and actual can be 426 * subject to widening conversion to formal. 427 */ 428 public static boolean isStrictInvocationConvertible(final Class<?> formal, 429 final Class<?> actual, 430 final boolean possibleVarArg) { 431 return isInvocationConvertible(formal, actual, true, possibleVarArg); 432 } 433 434 /** 435 * Checks whether a method accepts a variable number of arguments. 436 * <p>May be due to a subtle bug in some JVMs, if a varargs method is an override, depending on (perhaps) the 437 * class introspection order, the isVarargs flag on the method itself will be false. 438 * To circumvent the potential problem, fetch the method with the same signature from the super-classes, 439 * - which will be different if override -and get the varargs flag from it. 440 * @param method the method or constructor to check for varargs 441 * @return true if declared varargs, false otherwise 442 */ 443 public static boolean isVarArgs(final Executable method) { 444 if (method == null) { 445 return false; 446 } 447 if (method.isVarArgs()) { 448 return true; 449 } 450 // before climbing up the hierarchy, verify that the last parameter is an array 451 final Class<?>[] ptypes = method.getParameterTypes(); 452 if (ptypes.length == 0 || ptypes[ptypes.length - 1].getComponentType() == null) { 453 return false; 454 } 455 final String methodName = method.getName(); 456 // if this is an override, was it actually declared as varargs? 457 Class<?> clazz = method.getDeclaringClass(); 458 do { 459 try { 460 final Method m = clazz.getMethod(methodName, ptypes); 461 if (m.isVarArgs()) { 462 return true; 463 } 464 } catch (final NoSuchMethodException xignore) { 465 // this should not happen... 466 } 467 clazz = clazz.getSuperclass(); 468 } while(clazz != null); 469 return false; 470 } 471 472 /** 473 * Determines which method signature (represented by a class array) is more 474 * specific. This defines a partial ordering on the method signatures. 475 * 476 * @param a the arguments signature 477 * @param c1 first method signature to compare 478 * @param c2 second method signature to compare 479 * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if 480 * c1 is less specific than c2, INCOMPARABLE if they are incomparable. 481 */ 482 private static int moreSpecific(final Class<?>[] a, final Class<?>[] c1, final Class<?>[] c2) { 483 // compare lengths to handle comparisons where the size of the arrays 484 // doesn't match, but the methods are both applicable due to the fact 485 // that one is a varargs method 486 if (c1.length > a.length) { 487 return LESS_SPECIFIC; 488 } 489 if (c2.length > a.length) { 490 return MORE_SPECIFIC; 491 } 492 if (c1.length > c2.length) { 493 return MORE_SPECIFIC; 494 } 495 if (c2.length > c1.length) { 496 return LESS_SPECIFIC; 497 } 498 // same length, keep ultimate param offset for vararg checks 499 final int length = c1.length; 500 final int ultimate = c1.length - 1; 501 // ok, move on and compare those of equal lengths 502 for (int i = 0; i < length; ++i) { 503 if (c1[i] != c2[i]) { 504 final boolean last = i == ultimate; 505 // argument is null, prefer an Object param 506 if (a[i] == Void.class) { 507 if (c1[i] == Object.class && c2[i] != Object.class) { 508 return MORE_SPECIFIC; 509 } 510 if (c1[i] != Object.class && c2[i] == Object.class) { 511 return LESS_SPECIFIC; 512 } 513 } 514 // prefer primitive on non-null arg, non-primitive otherwise 515 boolean c1s = isPrimitive(c1[i], last); 516 boolean c2s = isPrimitive(c2[i], last); 517 if (c1s != c2s) { 518 return c1s == (a[i] != Void.class) ? MORE_SPECIFIC : LESS_SPECIFIC; 519 } 520 // if c2 can be converted to c1 but not the opposite, 521 // c1 is more specific than c2 522 c1s = isStrictConvertible(c2[i], c1[i], last); 523 c2s = isStrictConvertible(c1[i], c2[i], last); 524 if (c1s != c2s) { 525 return c1s ? MORE_SPECIFIC : LESS_SPECIFIC; 526 } 527 } 528 } 529 // Incomparable due to non-related arguments (i.e.foo(Runnable) vs. foo(Serializable)) 530 return INCOMPARABLE; 531 } 532 /** Converts a primitive type to its corresponding class. 533 * <p> 534 * If the argument type is primitive then we want to convert our 535 * primitive type signature to the corresponding Object type so 536 * introspection for methods with primitive types will work 537 * correctly. 538 * </p> 539 * @param parm a may-be primitive type class 540 * @return the equivalent object class 541 */ 542 static Class<?> primitiveClass(final Class<?> parm) { 543 // it was marginally faster to get from the map than call isPrimitive... 544 //if (!parm.isPrimitive()) return parm; 545 final Class<?>[] prim = CONVERTIBLES.get(parm); 546 return prim == null ? parm : prim[0]; 547 } 548 549 /** The hash code. */ 550 private final int hashCode; 551 /** The method name. */ 552 private final String method; 553 554 /** The parameters. */ 555 private final Class<?>[] params; 556 557 /** 558 * Creates a key from a method. 559 * @param aMethod the method to generate the key from. 560 */ 561 MethodKey(final Executable aMethod) { 562 this(aMethod.getName(), aMethod.getParameterTypes()); 563 } 564 /** 565 * Creates a key from a method name and a set of parameters. 566 * @param aMethod the method to generate the key from, class name for constructors 567 * @param args the intended method parameters 568 */ 569 MethodKey(final String aMethod, final Class<?>[] args) { 570 // !! keep this in sync with the other ctor (hash code) !! 571 this.method = aMethod.intern(); 572 int hash = this.method.hashCode(); 573 final int size; 574 // CSOFF: InnerAssignment 575 if (args != null && (size = args.length) > 0) { 576 this.params = new Class<?>[size]; 577 for (int p = 0; p < size; ++p) { 578 final Class<?> parm = primitiveClass(args[p]); 579 hash = HASH * hash + parm.hashCode(); 580 this.params[p] = parm; 581 } 582 } else { 583 this.params = NOARGS; 584 } 585 this.hashCode = hash; 586 } 587 /** 588 * Creates a key from a method name and a set of arguments. 589 * @param aMethod the method to generate the key from 590 * @param args the intended method arguments 591 */ 592 public MethodKey(final String aMethod, final Object[] args) { 593 // !! keep this in sync with the other ctor (hash code) !! 594 this.method = aMethod; 595 int hash = this.method.hashCode(); 596 final int size; 597 // CSOFF: InnerAssignment 598 if (args != null && (size = args.length) > 0) { 599 this.params = new Class<?>[size]; 600 for (int p = 0; p < size; ++p) { 601 final Object arg = args[p]; 602 // null arguments use void as Void.class as marker 603 final Class<?> parm = arg == null ? Void.class : arg.getClass(); 604 hash = HASH * hash + parm.hashCode(); 605 this.params[p] = parm; 606 } 607 } else { 608 this.params = NOARGS; 609 } 610 this.hashCode = hash; 611 } 612 613 /** 614 * Outputs a human-readable debug representation of this key. 615 * @return method(p0, p1, ...) 616 */ 617 public String debugString() { 618 final StringBuilder builder = new StringBuilder(method); 619 builder.append('('); 620 for (int i = 0; i < params.length; i++) { 621 if (i > 0) { 622 builder.append(", "); 623 } 624 builder.append(Void.class == params[i] ? "null" : params[i].getName()); 625 } 626 builder.append(')'); 627 return builder.toString(); 628 } 629 630 @Override 631 public boolean equals(final Object obj) { 632 if (obj instanceof MethodKey) { 633 final MethodKey key = (MethodKey) obj; 634 return method.equals(key.method) && Arrays.equals(params, key.params); 635 } 636 return false; 637 } 638 639 /** 640 * Gets this key's method name. 641 * @return the method name 642 */ 643 String getMethod() { 644 return method; 645 } 646 647 /** 648 * Gets the most specific method that is applicable to actual argument types.<p> 649 * Attempts to find the most specific applicable method using the 650 * algorithm described in the JLS section 15.12.2 (with the exception that it can't 651 * distinguish a primitive type argument from an object type argument, since in reflection 652 * primitive type arguments are represented by their object counterparts, so for an argument of 653 * type (say) java.lang.Integer, it will not be able to decide between a method that takes int and a 654 * method that takes java.lang.Integer as a parameter. 655 * </p> 656 * <p> 657 * This turns out to be a relatively rare case where this is needed - however, functionality 658 * like this is needed. 659 * </p> 660 * 661 * @param methods a list of methods 662 * @return the most specific method. 663 * @throws MethodKey.AmbiguousException if there is more than one. 664 */ 665 private <T extends Executable> T getMostSpecific(final T[] methods) { 666 final Class<?>[] args = getParameters(); 667 final Deque<T> applicables = getApplicables(methods, args); 668 if (applicables.isEmpty()) { 669 return null; 670 } 671 if (applicables.size() == 1) { 672 return applicables.getFirst(); 673 } 674 /* 675 * This list will contain the maximally specific methods. Hopefully at 676 * the end of the below loop, the list will contain exactly one method, 677 * (the most specific method) otherwise we have ambiguity. 678 */ 679 final Deque<T> maximals = new LinkedList<>(); 680 for (final T app : applicables) { 681 final Class<?>[] parms = app.getParameterTypes(); 682 boolean lessSpecific = false; 683 final Iterator<T> maximal = maximals.iterator(); 684 while (!lessSpecific && maximal.hasNext()) { 685 final T max = maximal.next(); 686 switch (moreSpecific(args, parms, max.getParameterTypes())) { 687 case MORE_SPECIFIC: 688 /* 689 * This method is more specific than the previously 690 * known maximally specific, so remove the old maximum. 691 */ 692 maximal.remove(); 693 break; 694 case LESS_SPECIFIC: 695 /* 696 * This method is less specific than any of the 697 * currently known maximally specific methods, so we 698 * won't add it into the set of maximally specific 699 * methods 700 */ 701 lessSpecific = true; 702 break; 703 default: 704 // nothing to do 705 } 706 } 707 if (!lessSpecific) { 708 maximals.addLast(app); 709 } 710 } 711 // if we have more than one maximally specific method, this call is ambiguous... 712 if (maximals.size() > 1) { 713 throw ambiguousException(args, applicables); 714 } 715 return maximals.getFirst(); 716 } // CSON: RedundantThrows 717 718 /** 719 * Gets the most specific constructor that is applicable to the parameters of this key. 720 * @param methods a list of constructors. 721 * @return the most specific constructor. 722 * @throws MethodKey.AmbiguousException if there is more than one. 723 */ 724 public Constructor<?> getMostSpecificConstructor(final Constructor<?>[] methods) { 725 return getMostSpecific(methods); 726 } 727 728 /** 729 * Gets the most specific method that is applicable to the parameters of this key. 730 * @param methods a list of methods. 731 * @return the most specific method. 732 * @throws MethodKey.AmbiguousException if there is more than one. 733 */ 734 public Method getMostSpecificMethod(final Method[] methods) { 735 return getMostSpecific(methods); 736 } 737 738 /** 739 * Gets this key's method parameter classes. 740 * @return the parameters 741 */ 742 Class<?>[] getParameters() { 743 return params; 744 } 745 746 @Override 747 public int hashCode() { 748 return hashCode; 749 } 750 751 @Override 752 public String toString() { 753 final StringBuilder builder = new StringBuilder(method); 754 for (final Class<?> c : params) { 755 builder.append(c == Void.class ? "null" : c.getName()); 756 } 757 return builder.toString(); 758 } 759 760 }