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