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