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