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