    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.lang3.reflect;
    018
    019import java.lang.annotation.Annotation;
    020import java.lang.reflect.Array;
    021import java.lang.reflect.InvocationTargetException;
    022import java.lang.reflect.Method;
    023import java.lang.reflect.Modifier;
    024import java.lang.reflect.Type;
    025import java.lang.reflect.TypeVariable;
    026import java.util.ArrayList;
    027import java.util.Arrays;
    028import java.util.Comparator;
    029import java.util.Iterator;
    030import java.util.LinkedHashSet;
    031import java.util.List;
    032import java.util.Map;
    033import java.util.Set;
    034import java.util.TreeMap;
    035import java.util.stream.Collectors;
    036
    037import org.apache.commons.lang3.ArrayUtils;
    038import org.apache.commons.lang3.ClassUtils;
    039import org.apache.commons.lang3.ClassUtils.Interfaces;
    040import org.apache.commons.lang3.Validate;
    041
    042import static java.util.stream.Collectors.toList;
    043
    044/**
    045 * <p>Utility reflection methods focused on {@link Method}s, originally from Commons BeanUtils.
    046 * Differences from the BeanUtils version may be noted, especially where similar functionality
    047 * already existed within Lang.
    048 * </p>
    049 *
    050 * <h2>Known Limitations</h2>
    051 * <h3>Accessing Public Methods In A Default Access Superclass</h3>
    052 * <p>There is an issue when invoking {@code public} methods contained in a default access superclass on JREs prior to 1.4.
    053 * Reflection locates these methods fine and correctly assigns them as {@code public}.
    054 * However, an {@link IllegalAccessException} is thrown if the method is invoked.</p>
    055 *
    056 * <p>{@link MethodUtils} contains a workaround for this situation.
    057 * It will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this method.
    058 * If this call succeeds, then the method can be invoked as normal.
    059 * This call will only succeed when the application has sufficient security privileges.
    060 * If this call fails then the method may fail.</p>
    061 *
    062 * @since 2.5
    063 */
    064public class MethodUtils {
    065
    066    private static final Comparator<Method> METHOD_BY_SIGNATURE = Comparator.comparing(Method::toString);
    067
    068    /**
    069     * <p>{@link MethodUtils} instances should NOT be constructed in standard programming.
    070     * Instead, the class should be used as
    071     * {@code MethodUtils.getAccessibleMethod(method)}.</p>
    072     *
    073     * <p>This constructor is {@code public} to permit tools that require a JavaBean
    074     * instance to operate.</p>
    075     */
    076    public MethodUtils() {
    077    }
    078
    079    /**
    080     * <p>Invokes a named method without parameters.</p>
    081     *
    082     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
    083     *
    084     * <p>This is a convenient wrapper for
    085     * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
    086     * </p>
    087     *
    088     * @param object invoke method on this object
    089     * @param methodName get method with this name
    090     * @return The value returned by the invoked method
    091     *
    092     * @throws NoSuchMethodException if there is no such accessible method
    093     * @throws InvocationTargetException wraps an exception thrown by the method invoked
    094     * @throws IllegalAccessException if the requested method is not accessible via reflection
    095     *
    096     *  @since 3.4
    097     */
    098    public static Object invokeMethod(final Object object, final String methodName) throws NoSuchMethodException,
    099            IllegalAccessException, InvocationTargetException {
    100        return invokeMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
    101    }
    102
    103    /**
    104     * <p>Invokes a named method without parameters.</p>
    105     *
    106     * <p>This is a convenient wrapper for
    107     * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
    108     * </p>
    109     *
    110     * @param object invoke method on this object
    111     * @param forceAccess force access to invoke method even if it's not accessible
    112     * @param methodName get method with this name
    113     * @return The value returned by the invoked method
    114     *
    115     * @throws NoSuchMethodException if there is no such accessible method
    116     * @throws InvocationTargetException wraps an exception thrown by the method invoked
    117     * @throws IllegalAccessException if the requested method is not accessible via reflection
    118     *
    119     * @since 3.5
    120     */
    121    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName)
    122            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    123        return invokeMethod(object, forceAccess, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
    124    }
    125
    126    /**
    127     * <p>Invokes a named method whose parameter type matches the object type.</p>
    128     *
    129     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
    130     *
    131     * <p>This method supports calls to methods taking primitive parameters
    132     * via passing in wrapping classes. So, for example, a {@code Boolean} object
    133     * would match a {@code boolean} primitive.</p>
    134     *
    135     * <p>This is a convenient wrapper for
    136     * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
    137     * </p>
    138     *
    139     * @param object invoke method on this object
    140     * @param methodName get method with this name
    141     * @param args use these arguments - treat null as empty array
    142     * @return The value returned by the invoked method
    143     *
    144     * @throws NoSuchMethodException if there is no such accessible method
    145     * @throws InvocationTargetException wraps an exception thrown by the method invoked
    146     * @throws IllegalAccessException if the requested method is not accessible via reflection
    147     */
    148    public static Object invokeMethod(final Object object, final String methodName,
    149            Object... args) throws NoSuchMethodException,
    150            IllegalAccessException, InvocationTargetException {
    151        args = ArrayUtils.nullToEmpty(args);
    152        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
    153        return invokeMethod(object, methodName, args, parameterTypes);
    154    }
    155
    156    /**
    157     * <p>Invokes a named method whose parameter type matches the object type.</p>
    158     *
    159     * <p>This method supports calls to methods taking primitive parameters
    160     * via passing in wrapping classes. So, for example, a {@code Boolean} object
    161     * would match a {@code boolean} primitive.</p>
    162     *
    163     * <p>This is a convenient wrapper for
    164     * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
    165     * </p>
    166     *
    167     * @param object invoke method on this object
    168     * @param forceAccess force access to invoke method even if it's not accessible
    169     * @param methodName get method with this name
    170     * @param args use these arguments - treat null as empty array
    171     * @return The value returned by the invoked method
    172     *
    173     * @throws NoSuchMethodException if there is no such accessible method
    174     * @throws InvocationTargetException wraps an exception thrown by the method invoked
    175     * @throws IllegalAccessException if the requested method is not accessible via reflection
    176     *
    177     * @since 3.5
    178     */
    179    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName,
    180            Object... args) throws NoSuchMethodException,
    181            IllegalAccessException, InvocationTargetException {
    182        args = ArrayUtils.nullToEmpty(args);
    183        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
    184        return invokeMethod(object, forceAccess, methodName, args, parameterTypes);
    185    }
    186
    187    /**
    188     * <p>Invokes a named method whose parameter type matches the object type.</p>
    189     *
    190     * <p>This method supports calls to methods taking primitive parameters
    191     * via passing in wrapping classes. So, for example, a {@code Boolean} object
    192     * would match a {@code boolean} primitive.</p>
    193     *
    194     * @param object invoke method on this object
    195     * @param forceAccess force access to invoke method even if it's not accessible
    196     * @param methodName get method with this name
    197     * @param args use these arguments - treat null as empty array
    198     * @param parameterTypes match these parameters - treat null as empty array
    199     * @return The value returned by the invoked method
    200     *
    201     * @throws NoSuchMethodException if there is no such accessible method
    202     * @throws InvocationTargetException wraps an exception thrown by the method invoked
    203     * @throws IllegalAccessException if the requested method is not accessible via reflection
    204     * @since 3.5
    205     */
    206    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName,
    207            Object[] args, Class<?>[] parameterTypes)
    208            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    209        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
    210        args = ArrayUtils.nullToEmpty(args);
    211
    212        final String messagePrefix;
    213        Method method = null;
    214
    215        if (forceAccess) {
    216            messagePrefix = "No such method: ";
    217            method = getMatchingMethod(object.getClass(),
    218                    methodName, parameterTypes);
    219            if (method != null && !method.isAccessible()) {
    220                method.setAccessible(true);
    221            }
    222        } else {
    223            messagePrefix = "No such accessible method: ";
    224            method = getMatchingAccessibleMethod(object.getClass(),
    225                    methodName, parameterTypes);
    226        }
    227
    228        if (method == null) {
    229            throw new NoSuchMethodException(messagePrefix
    230                    + methodName + "() on object: "
    231                    + object.getClass().getName());
    232        }
    233        args = toVarArgs(method, args);
    234
    235        return method.invoke(object, args);
    236    }
    237
    238    /**
    239     * <p>Invokes a named method whose parameter type matches the object type.</p>
    240     *
    241     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
    242     *
    243     * <p>This method supports calls to methods taking primitive parameters
    244     * via passing in wrapping classes. So, for example, a {@code Boolean} object
    245     * would match a {@code boolean} primitive.</p>
    246     *
    247     * @param object invoke method on this object
    248     * @param methodName get method with this name
    249     * @param args use these arguments - treat null as empty array
    250     * @param parameterTypes match these parameters - treat null as empty array
    251     * @return The value returned by the invoked method
    252     *
    253     * @throws NoSuchMethodException if there is no such accessible method
    254     * @throws InvocationTargetException wraps an exception thrown by the method invoked
    255     * @throws IllegalAccessException if the requested method is not accessible via reflection
    256     */
    257    public static Object invokeMethod(final Object object, final String methodName,
    258            final Object[] args, final Class<?>[] parameterTypes)
    259            throws NoSuchMethodException, IllegalAccessException,
    260            InvocationTargetException {
    261        return invokeMethod(object, false, methodName, args, parameterTypes);
    262    }
    263
    264    /**
    265     * <p>Invokes a method whose parameter types match exactly the object
    266     * types.</p>
    267     *
    268     * <p>This uses reflection to invoke the method obtained from a call to
    269     * {@link #getAccessibleMethod}(Class, String, Class[])}.</p>
    270     *
    271     * @param object invoke method on this object
    272     * @param methodName get method with this name
    273     * @return The value returned by the invoked method
    274     *
    275     * @throws NoSuchMethodException if there is no such accessible method
    276     * @throws InvocationTargetException wraps an exception thrown by the
    277     *  method invoked
    278     * @throws IllegalAccessException if the requested method is not accessible
    279     *  via reflection
    280     *
    281     * @since 3.4
    282     */
    283    public static Object invokeExactMethod(final Object object, final String methodName) throws NoSuchMethodException,
    284            IllegalAccessException, InvocationTargetException {
    285        return invokeExactMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
    286    }
    287
    288    /**
    289     * <p>Invokes a method with no parameters.</p>
    290     *
    291     * <p>This uses reflection to invoke the method obtained from a call to
    292     * {@link #getAccessibleMethod}(Class, String, Class[])}.</p>
    293     *
    294     * @param object invoke method on this object
    295     * @param methodName get method with this name
    296     * @param args use these arguments - treat null as empty array
    297     * @return The value returned by the invoked method
    298     *
    299     * @throws NoSuchMethodException if there is no such accessible method
    300     * @throws InvocationTargetException wraps an exception thrown by the
    301     *  method invoked
    302     * @throws IllegalAccessException if the requested method is not accessible
    303     *  via reflection
    304     */
    305    public static Object invokeExactMethod(final Object object, final String methodName,
    306            Object... args) throws NoSuchMethodException,
    307            IllegalAccessException, InvocationTargetException {
    308        args = ArrayUtils.nullToEmpty(args);
    309        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
    310        return invokeExactMethod(object, methodName, args, parameterTypes);
    311    }
    312
    313    /**
    314     * <p>Invokes a method whose parameter types match exactly the parameter
    315     * types given.</p>
    316     *
    317     * <p>This uses reflection to invoke the method obtained from a call to
    318     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
    319     *
    320     * @param object invoke method on this object
    321     * @param methodName get method with this name
    322     * @param args use these arguments - treat null as empty array
    323     * @param parameterTypes match these parameters - treat {@code null} as empty array
    324     * @return The value returned by the invoked method
    325     *
    326     * @throws NoSuchMethodException if there is no such accessible method
    327     * @throws InvocationTargetException wraps an exception thrown by the
    328     *  method invoked
    329     * @throws IllegalAccessException if the requested method is not accessible
    330     *  via reflection
    331     */
    332    public static Object invokeExactMethod(final Object object, final String methodName,
    333            Object[] args, Class<?>[] parameterTypes)
    334            throws NoSuchMethodException, IllegalAccessException,
    335            InvocationTargetException {
    336        args = ArrayUtils.nullToEmpty(args);
    337        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
    338        final Method method = getAccessibleMethod(object.getClass(), methodName,
    339                parameterTypes);
    340        if (method == null) {
    341            throw new NoSuchMethodException("No such accessible method: "
    342                    + methodName + "() on object: "
    343                    + object.getClass().getName());
    344        }
    345        return method.invoke(object, args);
    346    }
    347
    348    /**
    349     * <p>Invokes a {@code static} method whose parameter types match exactly the parameter
    350     * types given.</p>
    351     *
    352     * <p>This uses reflection to invoke the method obtained from a call to
    353     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
    354     *
    355     * @param cls invoke static method on this class
    356     * @param methodName get method with this name
    357     * @param args use these arguments - treat {@code null} as empty array
    358     * @param parameterTypes match these parameters - treat {@code null} as empty array
    359     * @return The value returned by the invoked method
    360     *
    361     * @throws NoSuchMethodException if there is no such accessible method
    362     * @throws InvocationTargetException wraps an exception thrown by the
    363     *  method invoked
    364     * @throws IllegalAccessException if the requested method is not accessible
    365     *  via reflection
    366     */
    367    public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName,
    368            Object[] args, Class<?>[] parameterTypes)
    369            throws NoSuchMethodException, IllegalAccessException,
    370            InvocationTargetException {
    371        args = ArrayUtils.nullToEmpty(args);
    372        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
    373        final Method method = getAccessibleMethod(cls, methodName, parameterTypes);
    374        if (method == null) {
    375            throw new NoSuchMethodException("No such accessible method: "
    376                    + methodName + "() on class: " + cls.getName());
    377        }
    378        return method.invoke(null, args);
    379    }
    380
    381    /**
    382     * <p>Invokes a named {@code static} method whose parameter type matches the object type.</p>
    383     *
    384     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
    385     *
    386     * <p>This method supports calls to methods taking primitive parameters
    387     * via passing in wrapping classes. So, for example, a {@code Boolean} class
    388     * would match a {@code boolean} primitive.</p>
    389     *
    390     * <p>This is a convenient wrapper for
    391     * {@link #invokeStaticMethod(Class, String, Object[], Class[])}.
    392     * </p>
    393     *
    394     * @param cls invoke static method on this class
    395     * @param methodName get method with this name
    396     * @param args use these arguments - treat {@code null} as empty array
    397     * @return The value returned by the invoked method
    398     *
    399     * @throws NoSuchMethodException if there is no such accessible method
    400     * @throws InvocationTargetException wraps an exception thrown by the
    401     *  method invoked
    402     * @throws IllegalAccessException if the requested method is not accessible
    403     *  via reflection
    404     */
    405    public static Object invokeStaticMethod(final Class<?> cls, final String methodName,
    406            Object... args) throws NoSuchMethodException,
    407            IllegalAccessException, InvocationTargetException {
    408        args = ArrayUtils.nullToEmpty(args);
    409        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
    410        return invokeStaticMethod(cls, methodName, args, parameterTypes);
    411    }
    412
    413    /**
    414     * <p>Invokes a named {@code static} method whose parameter type matches the object type.</p>
    415     *
    416     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
    417     *
    418     * <p>This method supports calls to methods taking primitive parameters
    419     * via passing in wrapping classes. So, for example, a {@code Boolean} class
    420     * would match a {@code boolean} primitive.</p>
    421     *
    422     *
    423     * @param cls invoke static method on this class
    424     * @param methodName get method with this name
    425     * @param args use these arguments - treat {@code null} as empty array
    426     * @param parameterTypes match these parameters - treat {@code null} as empty array
    427     * @return The value returned by the invoked method
    428     *
    429     * @throws NoSuchMethodException if there is no such accessible method
    430     * @throws InvocationTargetException wraps an exception thrown by the
    431     *  method invoked
    432     * @throws IllegalAccessException if the requested method is not accessible
    433     *  via reflection
    434     */
    435    public static Object invokeStaticMethod(final Class<?> cls, final String methodName,
    436            Object[] args, Class<?>[] parameterTypes)
    437            throws NoSuchMethodException, IllegalAccessException,
    438            InvocationTargetException {
    439        args = ArrayUtils.nullToEmpty(args);
    440        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
    441        final Method method = getMatchingAccessibleMethod(cls, methodName,
    442                parameterTypes);
    443        if (method == null) {
    444            throw new NoSuchMethodException("No such accessible method: "
    445                    + methodName + "() on class: " + cls.getName());
    446        }
    447        args = toVarArgs(method, args);
    448        return method.invoke(null, args);
    449    }
    450
    451    private static Object[] toVarArgs(final Method method, Object[] args) {
    452        if (method.isVarArgs()) {
    453            final Class<?>[] methodParameterTypes = method.getParameterTypes();
    454            args = getVarArgs(args, methodParameterTypes);
    455        }
    456        return args;
    457    }
    458
    459    /**
    460     * <p>Given an arguments array passed to a varargs method, return an array of arguments in the canonical form,
    461     * i.e. an array with the declared number of parameters, and whose last parameter is an array of the varargs type.
    462     * </p>
    463     *
    464     * @param args the array of arguments passed to the varags method
    465     * @param methodParameterTypes the declared array of method parameter types
    466     * @return an array of the variadic arguments passed to the method
    467     * @since 3.5
    468     */
    469    static Object[] getVarArgs(final Object[] args, final Class<?>[] methodParameterTypes) {
    470        if (args.length == methodParameterTypes.length && (args[args.length - 1] == null ||
    471                args[args.length - 1].getClass().equals(methodParameterTypes[methodParameterTypes.length - 1]))) {
    472            // The args array is already in the canonical form for the method.
    473            return args;
    474        }
    475
    476        // Construct a new array matching the method's declared parameter types.
    477        final Object[] newArgs = new Object[methodParameterTypes.length];
    478
    479        // Copy the normal (non-varargs) parameters
    480        System.arraycopy(args, 0, newArgs, 0, methodParameterTypes.length - 1);
    481
    482        // Construct a new array for the variadic parameters
    483        final Class<?> varArgComponentType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
    484        final int varArgLength = args.length - methodParameterTypes.length + 1;
    485
    486        Object varArgsArray = Array.newInstance(ClassUtils.primitiveToWrapper(varArgComponentType), varArgLength);
    487        // Copy the variadic arguments into the varargs array.
    488        System.arraycopy(args, methodParameterTypes.length - 1, varArgsArray, 0, varArgLength);
    489
    490        if (varArgComponentType.isPrimitive()) {
    491            // unbox from wrapper type to primitive type
    492            varArgsArray = ArrayUtils.toPrimitive(varArgsArray);
    493        }
    494
    495        // Store the varargs array in the last position of the array to return
    496        newArgs[methodParameterTypes.length - 1] = varArgsArray;
    497
    498        // Return the canonical varargs array.
    499        return newArgs;
    500    }
    501
    502    /**
    503     * <p>Invokes a {@code static} method whose parameter types match exactly the object
    504     * types.</p>
    505     *
    506     * <p>This uses reflection to invoke the method obtained from a call to
    507     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
    508     *
    509     * @param cls invoke static method on this class
    510     * @param methodName get method with this name
    511     * @param args use these arguments - treat {@code null} as empty array
    512     * @return The value returned by the invoked method
    513     *
    514     * @throws NoSuchMethodException if there is no such accessible method
    515     * @throws InvocationTargetException wraps an exception thrown by the
    516     *  method invoked
    517     * @throws IllegalAccessException if the requested method is not accessible
    518     *  via reflection
    519     */
    520    public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName,
    521            Object... args) throws NoSuchMethodException,
    522            IllegalAccessException, InvocationTargetException {
    523        args = ArrayUtils.nullToEmpty(args);
    524        final Class<?>[] parameterTypes = ClassUtils.toClass(args);
    525        return invokeExactStaticMethod(cls, methodName, args, parameterTypes);
    526    }
    527
    528    /**
    529     * <p>Returns an accessible method (that is, one that can be invoked via
    530     * reflection) with given name and parameters. If no such method
    531     * can be found, return {@code null}.
    532     * This is just a convenience wrapper for
    533     * {@link #getAccessibleMethod(Method)}.</p>
    534     *
    535     * @param cls get method from this class
    536     * @param methodName get method with this name
    537     * @param parameterTypes with these parameters types
    538     * @return The accessible method
    539     */
    540    public static Method getAccessibleMethod(final Class<?> cls, final String methodName,
    541            final Class<?>... parameterTypes) {
    542        try {
    543            return getAccessibleMethod(cls.getMethod(methodName,
    544                    parameterTypes));
    545        } catch (final NoSuchMethodException e) {
    546            return null;
    547        }
    548    }
    549
    550    /**
    551     * <p>Returns an accessible method (that is, one that can be invoked via
    552     * reflection) that implements the specified Method. If no such method
    553     * can be found, return {@code null}.</p>
    554     *
    555     * @param method The method that we wish to call
    556     * @return The accessible method
    557     */
    558    public static Method getAccessibleMethod(Method method) {
    559        if (!MemberUtils.isAccessible(method)) {
    560            return null;
    561        }
    562        // If the declaring class is public, we are done
    563        final Class<?> cls = method.getDeclaringClass();
    564        if (Modifier.isPublic(cls.getModifiers())) {
    565            return method;
    566        }
    567        final String methodName = method.getName();
    568        final Class<?>[] parameterTypes = method.getParameterTypes();
    569
    570        // Check the implemented interfaces and subinterfaces
    571        method = getAccessibleMethodFromInterfaceNest(cls, methodName,
    572                parameterTypes);
    573
    574        // Check the superclass chain
    575        if (method == null) {
    576            method = getAccessibleMethodFromSuperclass(cls, methodName,
    577                    parameterTypes);
    578        }
    579        return method;
    580    }
    581
    582    /**
    583     * <p>Returns an accessible method (that is, one that can be invoked via
    584     * reflection) by scanning through the superclasses. If no such method
    585     * can be found, return {@code null}.</p>
    586     *
    587     * @param cls Class to be checked
    588     * @param methodName Method name of the method we wish to call
    589     * @param parameterTypes The parameter type signatures
    590     * @return the accessible method or {@code null} if not found
    591     */
    592    private static Method getAccessibleMethodFromSuperclass(final Class<?> cls,
    593            final String methodName, final Class<?>... parameterTypes) {
    594        Class<?> parentClass = cls.getSuperclass();
    595        while (parentClass != null) {
    596            if (Modifier.isPublic(parentClass.getModifiers())) {
    597                try {
    598                    return parentClass.getMethod(methodName, parameterTypes);
    599                } catch (final NoSuchMethodException e) {
    600                    return null;
    601                }
    602            }
    603            parentClass = parentClass.getSuperclass();
    604        }
    605        return null;
    606    }
    607
    608    /**
    609     * <p>Returns an accessible method (that is, one that can be invoked via
    610     * reflection) that implements the specified method, by scanning through
    611     * all implemented interfaces and subinterfaces. If no such method
    612     * can be found, return {@code null}.</p>
    613     *
    614     * <p>There isn't any good reason why this method must be {@code private}.
    615     * It is because there doesn't seem any reason why other classes should
    616     * call this rather than the higher level methods.</p>
    617     *
    618     * @param cls Parent class for the interfaces to be checked
    619     * @param methodName Method name of the method we wish to call
    620     * @param parameterTypes The parameter type signatures
    621     * @return the accessible method or {@code null} if not found
    622     */
    623    private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls,
    624            final String methodName, final Class<?>... parameterTypes) {
    625        // Search up the superclass chain
    626        for (; cls != null; cls = cls.getSuperclass()) {
    627
    628            // Check the implemented interfaces of the parent class
    629            final Class<?>[] interfaces = cls.getInterfaces();
    630            for (final Class<?> anInterface : interfaces) {
    631                // Is this interface public?
    632                if (!Modifier.isPublic(anInterface.getModifiers())) {
    633                    continue;
    634                }
    635                // Does the method exist on this interface?
    636                try {
    637                    return anInterface.getDeclaredMethod(methodName,
    638                            parameterTypes);
    639                } catch (final NoSuchMethodException e) { // NOPMD
    640                    /*
    641                     * Swallow, if no method is found after the loop then this
    642                     * method returns null.
    643                     */
    644                }
    645                // Recursively check our parent interfaces
    646                final Method method = getAccessibleMethodFromInterfaceNest(anInterface,
    647                        methodName, parameterTypes);
    648                if (method != null) {
    649                    return method;
    650                }
    651            }
    652        }
    653        return null;
    654    }
    655
    656    /**
    657     * <p>Finds an accessible method that matches the given name and has compatible parameters.
    658     * Compatible parameters mean that every method parameter is assignable from
    659     * the given parameters.
    660     * In other words, it finds a method with the given name
    661     * that will take the parameters given.</p>
    662     *
    663     * <p>This method is used by
    664     * {@link
    665     * #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
    666     * </p>
    667     *
    668     * <p>This method can match primitive parameter by passing in wrapper classes.
    669     * For example, a {@code Boolean} will match a primitive {@code boolean}
    670     * parameter.
    671     * </p>
    672     *
    673     * @param cls find method in this class
    674     * @param methodName find method with this name
    675     * @param parameterTypes find method with most compatible parameters
    676     * @return The accessible method
    677     */
    678    public static Method getMatchingAccessibleMethod(final Class<?> cls,
    679            final String methodName, final Class<?>... parameterTypes) {
    680        try {
    681            final Method method = cls.getMethod(methodName, parameterTypes);
    682            MemberUtils.setAccessibleWorkaround(method);
    683            return method;
    684        } catch (final NoSuchMethodException e) { // NOPMD - Swallow the exception
    685        }
    686        // search through all methods
    687        final Method[] methods = cls.getMethods();
    688        final List<Method> matchingMethods = new ArrayList<>();
    689        for (final Method method : methods) {
    690            // compare name and parameters
    691            if (method.getName().equals(methodName) &&
    692                    MemberUtils.isMatchingMethod(method, parameterTypes)) {
    693                matchingMethods.add (method);
    694            }
    695        }
    696
    697        // Sort methods by signature to force deterministic result
    698        matchingMethods.sort(METHOD_BY_SIGNATURE);
    699
    700        Method bestMatch = null;
    701        for (final Method method : matchingMethods) {
    702            // get accessible version of method
    703            final Method accessibleMethod = getAccessibleMethod(method);
    704            if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareMethodFit(
    705                        accessibleMethod,
    706                        bestMatch,
    707                        parameterTypes) < 0)) {
    708                bestMatch = accessibleMethod;
    709            }
    710        }
    711        if (bestMatch != null) {
    712            MemberUtils.setAccessibleWorkaround(bestMatch);
    713        }
    714
    715        if (bestMatch != null && bestMatch.isVarArgs() && bestMatch.getParameterTypes().length > 0 && parameterTypes.length > 0) {
    716            final Class<?>[] methodParameterTypes = bestMatch.getParameterTypes();
    717            final Class<?> methodParameterComponentType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
    718            final String methodParameterComponentTypeName = ClassUtils.primitiveToWrapper(methodParameterComponentType).getName();
    719
    720            final Class<?> lastParameterType = parameterTypes[parameterTypes.length - 1];
    721            final String parameterTypeName = (lastParameterType==null) ? null : lastParameterType.getName();
    722            final String parameterTypeSuperClassName = (lastParameterType==null) ? null : lastParameterType.getSuperclass().getName();
    723
    724            if (parameterTypeName!= null && parameterTypeSuperClassName != null && !methodParameterComponentTypeName.equals(parameterTypeName)
    725                    && !methodParameterComponentTypeName.equals(parameterTypeSuperClassName)) {
    726                return null;
    727            }
    728        }
    729
    730        return bestMatch;
    731    }
    732
    733    /**
    734     * <p>Retrieves a method whether or not it's accessible. If no such method
    735     * can be found, return {@code null}.</p>
    736     * @param cls The class that will be subjected to the method search
    737     * @param methodName The method that we wish to call
    738     * @param parameterTypes Argument class types
    739     * @return The method
    740     *
    741     * @since 3.5
    742     */
    743    public static Method getMatchingMethod(final Class<?> cls, final String methodName,
    744            final Class<?>... parameterTypes) {
    745        Validate.notNull(cls, "cls");
    746        Validate.notEmpty(methodName, "methodName");
    747
    748        final List<Method> methods = Arrays.stream(cls.getDeclaredMethods())
    749                .filter(method -> method.getName().equals(methodName))
    750                .collect(toList());
    751
    752        ClassUtils.getAllSuperclasses(cls).stream()
    753                .map(Class::getDeclaredMethods)
    754                .flatMap(Arrays::stream)
    755                .filter(method -> method.getName().equals(methodName))
    756                .forEach(methods::add);
    757
    758        for (final Method method : methods) {
    759            if (Arrays.deepEquals(method.getParameterTypes(), parameterTypes)) {
    760                return method;
    761            }
    762        }
    763
    764        final TreeMap<Integer, List<Method>> candidates = new TreeMap<>();
    765
    766        methods.stream()
    767                .filter(method -> ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true))
    768                .forEach(method -> {
    769                    final int distance = distance(parameterTypes, method.getParameterTypes());
    770                    final List<Method> candidatesAtDistance = candidates.computeIfAbsent(distance, k -> new ArrayList<>());
    771                    candidatesAtDistance.add(method);
    772                });
    773
    774        if (candidates.isEmpty()) {
    775            return null;
    776        }
    777
    778        final List<Method> bestCandidates = candidates.values().iterator().next();
    779        if (bestCandidates.size() == 1) {
    780            return bestCandidates.get(0);
    781        }
    782
    783        throw new IllegalStateException(
    784                String.format("Found multiple candidates for method %s on class %s : %s",
    785                        methodName + Arrays.stream(parameterTypes).map(String::valueOf).collect(Collectors.joining(",", "(", ")")),
    786                        cls.getName(),
    787                        bestCandidates.stream().map(Method::toString).collect(Collectors.joining(",", "[", "]")))
    788        );
    789    }
    790
    791    /**
    792     * <p>Returns the aggregate number of inheritance hops between assignable argument class types.  Returns -1
    793     * if the arguments aren't assignable.  Fills a specific purpose for getMatchingMethod and is not generalized.</p>
    794     * @param fromClassArray the Class array to calculate the distance from.
    795     * @param toClassArray the Class array to calculate the distance to.
    796     * @return the aggregate number of inheritance hops between assignable argument class types.
    797     */
    798    private static int distance(final Class<?>[] fromClassArray, final Class<?>[] toClassArray) {
    799        int answer = 0;
    800
    801        if (!ClassUtils.isAssignable(fromClassArray, toClassArray, true)) {
    802            return -1;
    803        }
    804        for (int offset = 0; offset < fromClassArray.length; offset++) {
    805            // Note InheritanceUtils.distance() uses different scoring system.
    806            final Class<?> aClass = fromClassArray[offset];
    807            final Class<?> toClass = toClassArray[offset];
    808            if (aClass == null || aClass.equals(toClass)) {
    809                continue;
    810            } else if (ClassUtils.isAssignable(aClass, toClass, true)
    811                    && !ClassUtils.isAssignable(aClass, toClass, false)) {
    812                answer++;
    813            } else {
    814                answer = answer + 2;
    815            }
    816        }
    817
    818        return answer;
    819    }
    820
    821    /**
    822     * Gets the hierarchy of overridden methods down to {@code result} respecting generics.
    823     * @param method lowest to consider
    824     * @param interfacesBehavior whether to search interfaces, {@code null} {@code implies} false
    825     * @return Set&lt;Method&gt; in ascending order from sub- to superclass
    826     * @throws NullPointerException if the specified method is {@code null}
    827     * @since 3.2
    828     */
    829    public static Set<Method> getOverrideHierarchy(final Method method, final Interfaces interfacesBehavior) {
    830        Validate.notNull(method);
    831        final Set<Method> result = new LinkedHashSet<>();
    832        result.add(method);
    833
    834        final Class<?>[] parameterTypes = method.getParameterTypes();
    835
    836        final Class<?> declaringClass = method.getDeclaringClass();
    837
    838        final Iterator<Class<?>> hierarchy = ClassUtils.hierarchy(declaringClass, interfacesBehavior).iterator();
    839        //skip the declaring class :P
    840        hierarchy.next();
    841        hierarchyTraversal: while (hierarchy.hasNext()) {
    842            final Class<?> c = hierarchy.next();
    843            final Method m = getMatchingAccessibleMethod(c, method.getName(), parameterTypes);
    844            if (m == null) {
    845                continue;
    846            }
    847            if (Arrays.equals(m.getParameterTypes(), parameterTypes)) {
    848                // matches without generics
    849                result.add(m);
    850                continue;
    851            }
    852            // necessary to get arguments every time in the case that we are including interfaces
    853            final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(declaringClass, m.getDeclaringClass());
    854            for (int i = 0; i < parameterTypes.length; i++) {
    855                final Type childType = TypeUtils.unrollVariables(typeArguments, method.getGenericParameterTypes()[i]);
    856                final Type parentType = TypeUtils.unrollVariables(typeArguments, m.getGenericParameterTypes()[i]);
    857                if (!TypeUtils.equals(childType, parentType)) {
    858                    continue hierarchyTraversal;
    859                }
    860            }
    861            result.add(m);
    862        }
    863        return result;
    864    }
    865
    866    /**
    867     * Gets all class level public methods of the given class that are annotated with the given annotation.
    868     * @param cls
    869     *            the {@link Class} to query
    870     * @param annotationCls
    871     *            the {@link java.lang.annotation.Annotation} that must be present on a method to be matched
    872     * @return an array of Methods (possibly empty).
    873     * @throws NullPointerException if the class or annotation are {@code null}
    874     * @since 3.4
    875     */
    876    public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
    877        return getMethodsWithAnnotation(cls, annotationCls, false, false);
    878    }
    879
    880    /**
    881     * Gets all class level public methods of the given class that are annotated with the given annotation.
    882     * @param cls
    883     *            the {@link Class} to query
    884     * @param annotationCls
    885     *            the {@link Annotation} that must be present on a method to be matched
    886     * @return a list of Methods (possibly empty).
    887     * @throws IllegalArgumentException
    888     *            if the class or annotation are {@code null}
    889     * @since 3.4
    890     */
    891    public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
    892        return getMethodsListWithAnnotation(cls, annotationCls, false, false);
    893    }
    894
    895    /**
    896     * Gets all methods of the given class that are annotated with the given annotation.
    897     * @param cls
    898     *            the {@link Class} to query
    899     * @param annotationCls
    900     *            the {@link java.lang.annotation.Annotation} that must be present on a method to be matched
    901     * @param searchSupers
    902     *            determines if a lookup in the entire inheritance hierarchy of the given class should be performed
    903     * @param ignoreAccess
    904     *            determines if non public methods should be considered
    905     * @return an array of Methods (possibly empty).
    906     * @throws NullPointerException if the class or annotation are {@code null}
    907     * @since 3.6
    908     */
    909    public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls,
    910                                                    final boolean searchSupers, final boolean ignoreAccess) {
    911        final List<Method> annotatedMethodsList = getMethodsListWithAnnotation(cls, annotationCls, searchSupers,
    912                ignoreAccess);
    913        return annotatedMethodsList.toArray(ArrayUtils.EMPTY_METHOD_ARRAY);
    914    }
    915
    916    /**
    917     * Gets all methods of the given class that are annotated with the given annotation.
    918     * @param cls
    919     *            the {@link Class} to query
    920     * @param annotationCls
    921     *            the {@link Annotation} that must be present on a method to be matched
    922     * @param searchSupers
    923     *            determines if a lookup in the entire inheritance hierarchy of the given class should be performed
    924     * @param ignoreAccess
    925     *            determines if non public methods should be considered
    926     * @return a list of Methods (possibly empty).
    927     * @throws NullPointerException if either the class or annotation class is {@code null}
    928     * @since 3.6
    929     */
    930    public static List<Method> getMethodsListWithAnnotation(final Class<?> cls,
    931                                                            final Class<? extends Annotation> annotationCls,
    932                                                            final boolean searchSupers, final boolean ignoreAccess) {
    933
    934        Validate.notNull(cls, "cls");
    935        Validate.notNull(annotationCls, "annotationCls");
    936        final List<Class<?>> classes = (searchSupers ? getAllSuperclassesAndInterfaces(cls)
    937                : new ArrayList<>());
    938        classes.add(0, cls);
    939        final List<Method> annotatedMethods = new ArrayList<>();
    940        for (final Class<?> acls : classes) {
    941            final Method[] methods = (ignoreAccess ? acls.getDeclaredMethods() : acls.getMethods());
    942            for (final Method method : methods) {
    943                if (method.getAnnotation(annotationCls) != null) {
    944                    annotatedMethods.add(method);
    945                }
    946            }
    947        }
    948        return annotatedMethods;
    949    }
    950
    951    /**
    952     * <p>Gets the annotation object with the given annotation type that is present on the given method
    953     * or optionally on any equivalent method in super classes and interfaces. Returns null if the annotation
    954     * type was not present.</p>
    955     *
    956     * <p>Stops searching for an annotation once the first annotation of the specified type has been
    957     * found. Additional annotations of the specified type will be silently ignored.</p>
    958     * @param <A>
    959     *            the annotation type
    960     * @param method
    961     *            the {@link Method} to query
    962     * @param annotationCls
    963     *            the {@link Annotation} to check if is present on the method
    964     * @param searchSupers
    965     *            determines if a lookup in the entire inheritance hierarchy of the given class is performed
    966     *            if the annotation was not directly present
    967     * @param ignoreAccess
    968     *            determines if underlying method has to be accessible
    969     * @return the first matching annotation, or {@code null} if not found
    970     * @throws NullPointerException if either the method or annotation class is {@code null}
    971     * @since 3.6
    972     */
    973    public static <A extends Annotation> A getAnnotation(final Method method, final Class<A> annotationCls,
    974                                                         final boolean searchSupers, final boolean ignoreAccess) {
    975
    976        Validate.notNull(method, "method");
    977        Validate.notNull(annotationCls, "annotationCls");
    978        if (!ignoreAccess && !MemberUtils.isAccessible(method)) {
    979            return null;
    980        }
    981
    982        A annotation = method.getAnnotation(annotationCls);
    983
    984        if (annotation == null && searchSupers) {
    985            final Class<?> mcls = method.getDeclaringClass();
    986            final List<Class<?>> classes = getAllSuperclassesAndInterfaces(mcls);
    987            for (final Class<?> acls : classes) {
    988                final Method equivalentMethod = (ignoreAccess ? MethodUtils.getMatchingMethod(acls, method.getName(), method.getParameterTypes())
    989                    : MethodUtils.getMatchingAccessibleMethod(acls, method.getName(), method.getParameterTypes()));
    990                if (equivalentMethod != null) {
    991                    annotation = equivalentMethod.getAnnotation(annotationCls);
    992                    if (annotation != null) {
    993                        break;
    994                    }
    995                }
    996            }
    997        }
    998
    999        return annotation;
    1000    }
    1001
    1002    /**
    1003     * <p>Gets a combination of {@link ClassUtils#getAllSuperclasses(Class)} and
    1004     * {@link ClassUtils#getAllInterfaces(Class)}, one from superclasses, one
    1005     * from interfaces, and so on in a breadth first way.</p>
    1006     *
    1007     * @param cls  the class to look up, may be {@code null}
    1008     * @return the combined {@code List} of superclasses and interfaces in order
    1009     * going up from this one
    1010     *  {@code null} if null input
    1011     */
    1012    private static List<Class<?>> getAllSuperclassesAndInterfaces(final Class<?> cls) {
    1013        if (cls == null) {
    1014            return null;
    1015        }
    1016
    1017        final List<Class<?>> allSuperClassesAndInterfaces = new ArrayList<>();
    1018        final List<Class<?>> allSuperclasses = ClassUtils.getAllSuperclasses(cls);
    1019        int superClassIndex = 0;
    1020        final List<Class<?>> allInterfaces = ClassUtils.getAllInterfaces(cls);
    1021        int interfaceIndex = 0;
    1022        while (interfaceIndex < allInterfaces.size() ||
    1023                superClassIndex < allSuperclasses.size()) {
    1024            final Class<?> acls;
    1025            if (interfaceIndex >= allInterfaces.size()) {
    1026                acls = allSuperclasses.get(superClassIndex++);
    1027            } else if ((superClassIndex >= allSuperclasses.size()) || (interfaceIndex < superClassIndex) || !(superClassIndex < interfaceIndex)) {
    1028                acls = allInterfaces.get(interfaceIndex++);
    1029            } else {
    1030                acls = allSuperclasses.get(superClassIndex++);
    1031            }
    1032            allSuperClassesAndInterfaces.add(acls);
    1033        }
    1034        return allSuperClassesAndInterfaces;
    1035    }
    1036}