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