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