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 }