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