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