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