Source code
Description: source: package: org.apache.commons.lang3.reflect, class: MethodUtils

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