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           * 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 }