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