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 */ 017 package org.apache.commons.jexl2.internal.introspection; 018 019 import java.util.List; 020 import java.util.LinkedList; 021 import java.util.Iterator; 022 import java.lang.reflect.Constructor; 023 import java.lang.reflect.Method; 024 import java.util.Arrays; 025 026 /** 027 * A method key usable by the introspector cache. 028 * <p> 029 * This stores a method (or class) name and parameters. 030 * </p> 031 * <p> 032 * This replaces the original key scheme which used to build the key 033 * by concatenating the method name and parameters class names as one string 034 * with the exception that primitive types were converted to their object class equivalents. 035 * </p> 036 * <p> 037 * The key is still based on the same information, it is just wrapped in an object instead. 038 * Primitive type classes are converted to they object equivalent to make a key; 039 * int foo(int) and int foo(Integer) do generate the same key. 040 * </p> 041 * A key can be constructed either from arguments (array of objects) or from parameters 042 * (array of class). 043 * Roughly 3x faster than string key to access the map & uses less memory. 044 */ 045 public final class MethodKey { 046 /** The hash code. */ 047 private final int hashCode; 048 /** The method name. */ 049 private final String method; 050 /** The parameters. */ 051 private final Class<?>[] params; 052 /** A marker for empty parameter list. */ 053 private static final Class<?>[] NOARGS = new Class<?>[0]; 054 /** The hash code constants. */ 055 private static final int HASH = 37; 056 057 /** 058 * Creates a key from a method name and a set of arguments. 059 * @param aMethod the method to generate the key from 060 * @param args the intended method arguments 061 */ 062 public MethodKey(String aMethod, Object[] args) { 063 super(); 064 // !! keep this in sync with the other ctor (hash code) !! 065 this.method = aMethod; 066 int hash = this.method.hashCode(); 067 final int size; 068 // CSOFF: InnerAssignment 069 if (args != null && (size = args.length) > 0) { 070 this.params = new Class<?>[size]; 071 for (int p = 0; p < size; ++p) { 072 Object arg = args[p]; 073 // null arguments use void as Void.class as marker 074 Class<?> parm = arg == null ? Void.class : arg.getClass(); 075 hash = (HASH * hash) + parm.hashCode(); 076 this.params[p] = parm; 077 } 078 } else { 079 this.params = NOARGS; 080 } 081 this.hashCode = hash; 082 } 083 084 /** 085 * Creates a key from a method. 086 * @param aMethod the method to generate the key from. 087 */ 088 MethodKey(Method aMethod) { 089 this(aMethod.getName(), aMethod.getParameterTypes()); 090 } 091 092 /** 093 * Creates a key from a method name and a set of parameters. 094 * @param aMethod the method to generate the key from 095 * @param args the intended method parameters 096 */ 097 MethodKey(String aMethod, Class<?>[] args) { 098 super(); 099 // !! keep this in sync with the other ctor (hash code) !! 100 this.method = aMethod.intern(); 101 int hash = this.method.hashCode(); 102 final int size; 103 // CSOFF: InnerAssignment 104 if (args != null && (size = args.length) > 0) { 105 this.params = new Class<?>[size]; 106 for (int p = 0; p < size; ++p) { 107 Class<?> parm = ClassMap.MethodCache.primitiveClass(args[p]); 108 hash = (HASH * hash) + parm.hashCode(); 109 this.params[p] = parm; 110 } 111 } else { 112 this.params = NOARGS; 113 } 114 this.hashCode = hash; 115 } 116 117 /** 118 * Gets this key's method name. 119 * @return the method name 120 */ 121 String getMethod() { 122 return method; 123 } 124 125 /** 126 * Gets this key's method parameter classes. 127 * @return the parameters 128 */ 129 Class<?>[] getParameters() { 130 return params; 131 } 132 133 /** {@inheritDoc} */ 134 @Override 135 public int hashCode() { 136 return hashCode; 137 } 138 139 /** {@inheritDoc} */ 140 @Override 141 public boolean equals(Object obj) { 142 if (obj instanceof MethodKey) { 143 MethodKey key = (MethodKey) obj; 144 return method.equals(key.method) && Arrays.equals(params, key.params); 145 } 146 return false; 147 } 148 149 /** {@inheritDoc} */ 150 @Override 151 public String toString() { 152 StringBuilder builder = new StringBuilder(method); 153 for (Class<?> c : params) { 154 builder.append(c == Void.class ? "null" : c.getName()); 155 } 156 return builder.toString(); 157 } 158 159 /** 160 * Outputs a human readable debug representation of this key. 161 * @return method(p0, p1, ...) 162 */ 163 public String debugString() { 164 StringBuilder builder = new StringBuilder(method); 165 builder.append('('); 166 for (int i = 0; i < params.length; i++) { 167 if (i > 0) { 168 builder.append(", "); 169 } 170 builder.append(Void.class == params[i] ? "null" : params[i].getName()); 171 } 172 builder.append(')'); 173 return builder.toString(); 174 } 175 176 /** 177 * Gets the most specific method that is applicable to the parameters of this key. 178 * @param methods a list of methods. 179 * @return the most specific method. 180 * @throws MethodKey.AmbiguousException if there is more than one. 181 */ 182 public Method getMostSpecificMethod(List<Method> methods) { 183 return METHODS.getMostSpecific(methods, params); 184 } 185 186 /** 187 * Gets the most specific constructor that is applicable to the parameters of this key. 188 * @param methods a list of constructors. 189 * @return the most specific constructor. 190 * @throws MethodKey.AmbiguousException if there is more than one. 191 */ 192 public Constructor<?> getMostSpecificConstructor(List<Constructor<?>> methods) { 193 return CONSTRUCTORS.getMostSpecific(methods, params); 194 } 195 196 /** 197 * Determines whether a type represented by a class object is 198 * convertible to another type represented by a class object using a 199 * method invocation conversion, treating object types of primitive 200 * types as if they were primitive types (that is, a Boolean actual 201 * parameter type matches boolean primitive formal type). This behavior 202 * is because this method is used to determine applicable methods for 203 * an actual parameter list, and primitive types are represented by 204 * their object duals in reflective method calls. 205 * 206 * @param formal the formal parameter type to which the actual 207 * parameter type should be convertible 208 * @param actual the actual parameter type. 209 * @param possibleVarArg whether or not we're dealing with the last parameter 210 * in the method declaration 211 * @return true if either formal type is assignable from actual type, 212 * or formal is a primitive type and actual is its corresponding object 213 * type or an object type of a primitive type that can be converted to 214 * the formal type. 215 */ 216 public static boolean isInvocationConvertible(Class<?> formal, 217 Class<?> actual, 218 boolean possibleVarArg) { 219 /* if it's a null, it means the arg was null */ 220 if (actual == null && !formal.isPrimitive()) { 221 return true; 222 } 223 224 /* Check for identity or widening reference conversion */ 225 if (actual != null && formal.isAssignableFrom(actual)) { 226 return true; 227 } 228 229 /* Check for boxing with widening primitive conversion. Note that 230 * actual parameters are never primitives. */ 231 if (formal.isPrimitive()) { 232 if (formal == Boolean.TYPE && actual == Boolean.class) { 233 return true; 234 } 235 if (formal == Character.TYPE && actual == Character.class) { 236 return true; 237 } 238 if (formal == Byte.TYPE && actual == Byte.class) { 239 return true; 240 } 241 if (formal == Short.TYPE 242 && (actual == Short.class || actual == Byte.class)) { 243 return true; 244 } 245 if (formal == Integer.TYPE 246 && (actual == Integer.class || actual == Short.class 247 || actual == Byte.class)) { 248 return true; 249 } 250 if (formal == Long.TYPE 251 && (actual == Long.class || actual == Integer.class 252 || actual == Short.class || actual == Byte.class)) { 253 return true; 254 } 255 if (formal == Float.TYPE 256 && (actual == Float.class || actual == Long.class 257 || actual == Integer.class || actual == Short.class 258 || actual == Byte.class)) { 259 return true; 260 } 261 if (formal == Double.TYPE 262 && (actual == Double.class || actual == Float.class 263 || actual == Long.class || actual == Integer.class 264 || actual == Short.class || actual == Byte.class)) { 265 return true; 266 } 267 } 268 269 /* Check for vararg conversion. */ 270 if (possibleVarArg && formal.isArray()) { 271 if (actual != null && actual.isArray()) { 272 actual = actual.getComponentType(); 273 } 274 return isInvocationConvertible(formal.getComponentType(), 275 actual, false); 276 } 277 return false; 278 } 279 280 /** 281 * Determines whether a type represented by a class object is 282 * convertible to another type represented by a class object using a 283 * method invocation conversion, without matching object and primitive 284 * types. This method is used to determine the more specific type when 285 * comparing signatures of methods. 286 * 287 * @param formal the formal parameter type to which the actual 288 * parameter type should be convertible 289 * @param actual the actual parameter type. 290 * @param possibleVarArg whether or not we're dealing with the last parameter 291 * in the method declaration 292 * @return true if either formal type is assignable from actual type, 293 * or formal and actual are both primitive types and actual can be 294 * subject to widening conversion to formal. 295 */ 296 public static boolean isStrictInvocationConvertible(Class<?> formal, 297 Class<?> actual, 298 boolean possibleVarArg) { 299 /* we shouldn't get a null into, but if so */ 300 if (actual == null && !formal.isPrimitive()) { 301 return true; 302 } 303 304 /* Check for identity or widening reference conversion */ 305 if (formal.isAssignableFrom(actual)) { 306 return true; 307 } 308 309 /* Check for widening primitive conversion. */ 310 if (formal.isPrimitive()) { 311 if (formal == Short.TYPE && (actual == Byte.TYPE)) { 312 return true; 313 } 314 if (formal == Integer.TYPE 315 && (actual == Short.TYPE || actual == Byte.TYPE)) { 316 return true; 317 } 318 if (formal == Long.TYPE 319 && (actual == Integer.TYPE || actual == Short.TYPE 320 || actual == Byte.TYPE)) { 321 return true; 322 } 323 if (formal == Float.TYPE 324 && (actual == Long.TYPE || actual == Integer.TYPE 325 || actual == Short.TYPE || actual == Byte.TYPE)) { 326 return true; 327 } 328 if (formal == Double.TYPE 329 && (actual == Float.TYPE || actual == Long.TYPE 330 || actual == Integer.TYPE || actual == Short.TYPE 331 || actual == Byte.TYPE)) { 332 return true; 333 } 334 } 335 336 /* Check for vararg conversion. */ 337 if (possibleVarArg && formal.isArray()) { 338 if (actual != null && actual.isArray()) { 339 actual = actual.getComponentType(); 340 } 341 return isStrictInvocationConvertible(formal.getComponentType(), 342 actual, false); 343 } 344 return false; 345 } 346 347 /** 348 * whether a method/ctor is more specific than a previously compared one. 349 */ 350 private static final int MORE_SPECIFIC = 0; 351 /** 352 * whether a method/ctor is less specific than a previously compared one. 353 */ 354 private static final int LESS_SPECIFIC = 1; 355 /** 356 * A method/ctor doesn't match a previously compared one. 357 */ 358 private static final int INCOMPARABLE = 2; 359 360 /** 361 * Simple distinguishable exception, used when 362 * we run across ambiguous overloading. Caught 363 * by the introspector. 364 */ 365 public static class AmbiguousException extends RuntimeException { 366 /** 367 * Version Id for serializable. 368 */ 369 private static final long serialVersionUID = -2314636505414551664L; 370 } 371 372 /** 373 * Utility for parameters matching. 374 * @param <T> Method or Constructor 375 */ 376 private abstract static class Parameters<T> { 377 /** 378 * Extract the parameter types from its applicable argument. 379 * @param app a method or constructor 380 * @return the parameters 381 */ 382 protected abstract Class<?>[] getParameterTypes(T app); 383 384 // CSOFF: RedundantThrows 385 /** 386 * Gets the most specific method that is applicable to actual argument types. 387 * @param methods a list of methods. 388 * @param classes list of argument types. 389 * @return the most specific method. 390 * @throws MethodKey.AmbiguousException if there is more than one. 391 */ 392 private T getMostSpecific(List<T> methods, Class<?>[] classes) { 393 LinkedList<T> applicables = getApplicables(methods, classes); 394 395 if (applicables.isEmpty()) { 396 return null; 397 } 398 399 if (applicables.size() == 1) { 400 return applicables.getFirst(); 401 } 402 403 /* 404 * This list will contain the maximally specific methods. Hopefully at 405 * the end of the below loop, the list will contain exactly one method, 406 * (the most specific method) otherwise we have ambiguity. 407 */ 408 409 LinkedList<T> maximals = new LinkedList<T>(); 410 411 for (Iterator<T> applicable = applicables.iterator(); 412 applicable.hasNext();) { 413 T app = applicable.next(); 414 Class<?>[] appArgs = getParameterTypes(app); 415 416 boolean lessSpecific = false; 417 418 for (Iterator<T> maximal = maximals.iterator(); 419 !lessSpecific && maximal.hasNext();) { 420 T max = maximal.next(); 421 422 // CSOFF: MissingSwitchDefault 423 switch (moreSpecific(appArgs, getParameterTypes(max))) { 424 case MORE_SPECIFIC: 425 /* 426 * This method is more specific than the previously 427 * known maximally specific, so remove the old maximum. 428 */ 429 maximal.remove(); 430 break; 431 432 case LESS_SPECIFIC: 433 /* 434 * This method is less specific than some of the 435 * currently known maximally specific methods, so we 436 * won't add it into the set of maximally specific 437 * methods 438 */ 439 440 lessSpecific = true; 441 break; 442 } 443 } // CSON: MissingSwitchDefault 444 445 if (!lessSpecific) { 446 maximals.addLast(app); 447 } 448 } 449 if (maximals.size() > 1) { 450 // We have more than one maximally specific method 451 throw new AmbiguousException(); 452 } 453 return maximals.getFirst(); 454 } // CSON: RedundantThrows 455 456 /** 457 * Determines which method signature (represented by a class array) is more 458 * specific. This defines a partial ordering on the method signatures. 459 * 460 * @param c1 first signature to compare 461 * @param c2 second signature to compare 462 * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if 463 * c1 is less specific than c2, INCOMPARABLE if they are incomparable. 464 */ 465 private int moreSpecific(Class<?>[] c1, Class<?>[] c2) { 466 boolean c1MoreSpecific = false; 467 boolean c2MoreSpecific = false; 468 469 // compare lengths to handle comparisons where the size of the arrays 470 // doesn't match, but the methods are both applicable due to the fact 471 // that one is a varargs method 472 if (c1.length > c2.length) { 473 return MORE_SPECIFIC; 474 } 475 if (c2.length > c1.length) { 476 return LESS_SPECIFIC; 477 } 478 479 // ok, move on and compare those of equal lengths 480 for (int i = 0; i < c1.length; ++i) { 481 if (c1[i] != c2[i]) { 482 boolean last = (i == c1.length - 1); 483 c1MoreSpecific = c1MoreSpecific || isStrictConvertible(c2[i], c1[i], last); 484 c2MoreSpecific = c2MoreSpecific || isStrictConvertible(c1[i], c2[i], last); 485 } 486 } 487 488 if (c1MoreSpecific) { 489 if (c2MoreSpecific) { 490 // Incomparable due to cross-assignable arguments (i.e. foo(String, Object) vs. foo(Object, String)) 491 return INCOMPARABLE; 492 } 493 return MORE_SPECIFIC; 494 } 495 if (c2MoreSpecific) { 496 return LESS_SPECIFIC; 497 } 498 499 // attempt to choose by picking the one with the greater number of primitives or latest primitive parameter 500 int primDiff = 0; 501 for (int c = 0; c < c1.length; ++c) { 502 if (c1[c].isPrimitive()) { 503 primDiff += 1 << c; 504 } 505 if (c2[c].isPrimitive()) { 506 primDiff -= 1 << c; 507 } 508 } 509 if (primDiff > 0) { 510 return MORE_SPECIFIC; 511 } else if (primDiff < 0) { 512 return LESS_SPECIFIC; 513 } 514 /* 515 * Incomparable due to non-related arguments (i.e. 516 * foo(Runnable) vs. foo(Serializable)) 517 */ 518 return INCOMPARABLE; 519 } 520 521 /** 522 * Returns all methods that are applicable to actual argument types. 523 * 524 * @param methods list of all candidate methods 525 * @param classes the actual types of the arguments 526 * @return a list that contains only applicable methods (number of 527 * formal and actual arguments matches, and argument types are assignable 528 * to formal types through a method invocation conversion). 529 */ 530 private LinkedList<T> getApplicables(List<T> methods, Class<?>[] classes) { 531 LinkedList<T> list = new LinkedList<T>(); 532 533 for (Iterator<T> imethod = methods.iterator(); imethod.hasNext();) { 534 T method = imethod.next(); 535 if (isApplicable(method, classes)) { 536 list.add(method); 537 } 538 539 } 540 return list; 541 } 542 543 /** 544 * Returns true if the supplied method is applicable to actual 545 * argument types. 546 * 547 * @param method method that will be called 548 * @param classes arguments to method 549 * @return true if method is applicable to arguments 550 */ 551 private boolean isApplicable(T method, Class<?>[] classes) { 552 Class<?>[] methodArgs = getParameterTypes(method); 553 // if samee number or args or 554 // there's just one more methodArg than class arg 555 // and the last methodArg is an array, then treat it as a vararg 556 if (methodArgs.length == classes.length 557 || methodArgs.length == classes.length + 1 && methodArgs[methodArgs.length - 1].isArray()) { 558 // this will properly match when the last methodArg 559 // is an array/varargs and the last class is the type of array 560 // (e.g. String when the method is expecting String...) 561 for (int i = 0; i < classes.length; ++i) { 562 if (!isConvertible(methodArgs[i], classes[i], false)) { 563 // if we're on the last arg and the method expects an array 564 if (i == classes.length - 1 && methodArgs[i].isArray()) { 565 // check to see if the last arg is convertible 566 // to the array's component type 567 return isConvertible(methodArgs[i], classes[i], true); 568 } 569 return false; 570 } 571 } 572 return true; 573 } 574 // more arguments given than the method accepts; check for varargs 575 if (methodArgs.length > 0) { 576 // check that the last methodArg is an array 577 Class<?> lastarg = methodArgs[methodArgs.length - 1]; 578 if (!lastarg.isArray()) { 579 return false; 580 } 581 582 // check that they all match up to the last method arg 583 for (int i = 0; i < methodArgs.length - 1; ++i) { 584 if (!isConvertible(methodArgs[i], classes[i], false)) { 585 return false; 586 } 587 } 588 589 // check that all remaining arguments are convertible to the vararg type 590 Class<?> vararg = lastarg.getComponentType(); 591 for (int i = methodArgs.length - 1; i < classes.length; ++i) { 592 if (!isConvertible(vararg, classes[i], false)) { 593 return false; 594 } 595 } 596 return true; 597 } 598 // no match 599 return false; 600 } 601 602 /** 603 * @see #isInvocationConvertible(Class, Class, boolean) 604 * @param formal the formal parameter type to which the actual 605 * parameter type should be convertible 606 * @param actual the actual parameter type. 607 * @param possibleVarArg whether or not we're dealing with the last parameter 608 * in the method declaration 609 * @return see isMethodInvocationConvertible. 610 */ 611 private boolean isConvertible(Class<?> formal, Class<?> actual, 612 boolean possibleVarArg) { 613 // if we see Void.class, the argument was null 614 return isInvocationConvertible(formal, actual.equals(Void.class) ? null : actual, possibleVarArg); 615 } 616 617 /** 618 * @see #isStrictInvocationConvertible(Class, Class, boolean) 619 * @param formal the formal parameter type to which the actual 620 * parameter type should be convertible 621 * @param actual the actual parameter type. 622 * @param possibleVarArg whether or not we're dealing with the last parameter 623 * in the method declaration 624 * @return see isStrictMethodInvocationConvertible. 625 */ 626 private boolean isStrictConvertible(Class<?> formal, Class<?> actual, 627 boolean possibleVarArg) { 628 // if we see Void.class, the argument was null 629 return isStrictInvocationConvertible(formal, actual.equals(Void.class) ? null : actual, possibleVarArg); 630 } 631 } 632 633 /** 634 * The parameter matching service for methods. 635 */ 636 private static final Parameters<Method> METHODS = new Parameters<Method>() { 637 @Override 638 protected Class<?>[] getParameterTypes(Method app) { 639 return app.getParameterTypes(); 640 } 641 }; 642 643 /** 644 * The parameter matching service for constructors. 645 */ 646 private static final Parameters<Constructor<?>> CONSTRUCTORS = new Parameters<Constructor<?>>() { 647 @Override 648 protected Class<?>[] getParameterTypes(Constructor<?> app) { 649 return app.getParameterTypes(); 650 } 651 }; 652 }