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