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