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 *      https://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() || superClassIndex < allSuperclasses.size() && superClassIndex < interfaceIndex) {
237                acls = allSuperclasses.get(superClassIndex++);
238            } else {
239                acls = allInterfaces.get(interfaceIndex++);
240            }
241            allSuperClassesAndInterfaces.add(acls);
242        }
243        return allSuperClassesAndInterfaces;
244    }
245
246    /**
247     * Gets the annotation object with the given annotation type that is present on the given method
248     * or optionally on any equivalent method in super classes and interfaces. Returns null if the annotation
249     * type was not present.
250     *
251     * <p>Stops searching for an annotation once the first annotation of the specified type has been
252     * found. Additional annotations of the specified type will be silently ignored.</p>
253     * @param <A>
254     *            the annotation type
255     * @param method
256     *            the {@link Method} to query, may be null.
257     * @param annotationCls
258     *            the {@link Annotation} to check if is present on the method
259     * @param searchSupers
260     *            determines if a lookup in the entire inheritance hierarchy of the given class is performed
261     *            if the annotation was not directly present
262     * @param ignoreAccess
263     *            determines if underlying method has to be accessible
264     * @return the first matching annotation, or {@code null} if not found
265     * @throws NullPointerException if either the method or annotation class is {@code null}
266     * @throws SecurityException if an underlying accessible object's method denies the request.
267     * @see SecurityManager#checkPermission
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        Objects.requireNonNull(method, "method");
273        Objects.requireNonNull(annotationCls, "annotationCls");
274        if (!ignoreAccess && !MemberUtils.isAccessible(method)) {
275            return null;
276        }
277        A annotation = method.getAnnotation(annotationCls);
278        if (annotation == null && searchSupers) {
279            final Class<?> mcls = method.getDeclaringClass();
280            final List<Class<?>> classes = getAllSuperclassesAndInterfaces(mcls);
281            for (final Class<?> acls : classes) {
282                final Method equivalentMethod = ignoreAccess ? getMatchingMethod(acls, method.getName(), method.getParameterTypes())
283                        : getMatchingAccessibleMethod(acls, method.getName(), method.getParameterTypes());
284                if (equivalentMethod != null) {
285                    annotation = equivalentMethod.getAnnotation(annotationCls);
286                    if (annotation != null) {
287                        break;
288                    }
289                }
290            }
291        }
292        return annotation;
293    }
294
295    /**
296     * Gets an accessible method that matches the given name and has compatible parameters.
297     * Compatible parameters mean that every method parameter is assignable from
298     * the given parameters.
299     * In other words, it finds a method with the given name
300     * that will take the parameters given.
301     *
302     * <p>This method is used by
303     * {@link
304     * #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
305     * </p>
306     *
307     * <p>This method can match primitive parameter by passing in wrapper classes.
308     * For example, a {@link Boolean} will match a primitive {@code boolean}
309     * parameter.
310     * </p>
311     *
312     * @param cls find method in this class
313     * @param methodName find method with this name
314     * @param parameterTypes find method with most compatible parameters
315     * @return The accessible method
316     * @throws SecurityException if an underlying accessible object's method denies the request.
317     * @see SecurityManager#checkPermission
318     */
319    public static Method getMatchingAccessibleMethod(final Class<?> cls,
320        final String methodName, final Class<?>... parameterTypes) {
321        final Method candidate = getMethodObject(cls, methodName, parameterTypes);
322        if (candidate != null) {
323            return MemberUtils.setAccessibleWorkaround(candidate);
324        }
325        // search through all methods
326        final Method[] methods = cls.getMethods();
327        final List<Method> matchingMethods = Stream.of(methods)
328            .filter(method -> method.getName().equals(methodName) && MemberUtils.isMatchingMethod(method, parameterTypes)).collect(Collectors.toList());
329
330        // Sort methods by signature to force deterministic result
331        matchingMethods.sort(METHOD_BY_SIGNATURE);
332
333        Method bestMatch = null;
334        for (final Method method : matchingMethods) {
335            // get accessible version of method
336            final Method accessibleMethod = getAccessibleMethod(method);
337            if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareMethodFit(accessibleMethod, bestMatch, parameterTypes) < 0)) {
338                bestMatch = accessibleMethod;
339            }
340        }
341        if (bestMatch != null) {
342            MemberUtils.setAccessibleWorkaround(bestMatch);
343        }
344
345        if (bestMatch != null && bestMatch.isVarArgs() && bestMatch.getParameterTypes().length > 0 && parameterTypes.length > 0) {
346            final Class<?>[] methodParameterTypes = bestMatch.getParameterTypes();
347            final Class<?> methodParameterComponentType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
348            final String methodParameterComponentTypeName = ClassUtils.primitiveToWrapper(methodParameterComponentType).getName();
349
350            final Class<?> lastParameterType = parameterTypes[parameterTypes.length - 1];
351            final String parameterTypeName = lastParameterType == null ? null : lastParameterType.getName();
352            final String parameterTypeSuperClassName = lastParameterType == null ? null
353                    : lastParameterType.getSuperclass() != null ? lastParameterType.getSuperclass().getName() : null;
354
355            if (parameterTypeName != null && parameterTypeSuperClassName != null && !methodParameterComponentTypeName.equals(parameterTypeName)
356                && !methodParameterComponentTypeName.equals(parameterTypeSuperClassName)) {
357                return null;
358            }
359        }
360
361        return bestMatch;
362    }
363
364    /**
365     * Gets a method whether or not it's accessible. If no such method
366     * can be found, return {@code null}.
367     *
368     * @param cls The class that will be subjected to the method search
369     * @param methodName The method that we wish to call
370     * @param parameterTypes Argument class types
371     * @throws IllegalStateException if there is no unique result
372     * @throws NullPointerException if the class is {@code null}
373     * @return The method
374     * @since 3.5
375     */
376    public static Method getMatchingMethod(final Class<?> cls, final String methodName,
377            final Class<?>... parameterTypes) {
378        Objects.requireNonNull(cls, "cls");
379        Validate.notEmpty(methodName, "methodName");
380
381        final List<Method> methods = Stream.of(cls.getDeclaredMethods())
382                .filter(method -> method.getName().equals(methodName))
383                .collect(Collectors.toList());
384
385        getAllSuperclassesAndInterfaces(cls).stream()
386                .map(Class::getDeclaredMethods)
387                .flatMap(Stream::of)
388                .filter(method -> method.getName().equals(methodName))
389                .forEach(methods::add);
390
391        for (final Method method : methods) {
392            if (Arrays.deepEquals(method.getParameterTypes(), parameterTypes)) {
393                return method;
394            }
395        }
396
397        final TreeMap<Integer, List<Method>> candidates = new TreeMap<>();
398
399        methods.stream()
400                .filter(method -> ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true))
401                .forEach(method -> {
402                    final int distance = distance(parameterTypes, method.getParameterTypes());
403                    final List<Method> candidatesAtDistance = candidates.computeIfAbsent(distance, k -> new ArrayList<>());
404                    candidatesAtDistance.add(method);
405                });
406
407        if (candidates.isEmpty()) {
408            return null;
409        }
410
411        final List<Method> bestCandidates = candidates.values().iterator().next();
412        if (bestCandidates.size() == 1 || !Objects.equals(bestCandidates.get(0).getDeclaringClass(),
413                bestCandidates.get(1).getDeclaringClass())) {
414            return bestCandidates.get(0);
415        }
416
417        throw new IllegalStateException(
418                String.format("Found multiple candidates for method %s on class %s : %s",
419                        methodName + Stream.of(parameterTypes).map(String::valueOf).collect(Collectors.joining(",", "(", ")")),
420                        cls.getName(),
421                        bestCandidates.stream().map(Method::toString).collect(Collectors.joining(",", "[", "]")))
422        );
423    }
424
425    /**
426     * Gets a Method or null if a {@link Class#getMethod(String, Class...) documented} exception is thrown.
427     *
428     * @param cls Receiver for {@link Class#getMethod(String, Class...)}.
429     * @param name the name of the method
430     * @param parameterTypes the list of parameters
431     * @return a Method or null.
432     * @throws SecurityException if an underlying accessible object's method denies the request.
433     * @see SecurityManager#checkPermission
434     * @see Class#getMethod(String, Class...)
435     * @since 3.15.0
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     * @throws SecurityException if an underlying accessible object's method denies the request.
534     * @see SecurityManager#checkPermission
535     * @since 3.2
536     */
537    public static Set<Method> getOverrideHierarchy(final Method method, final Interfaces interfacesBehavior) {
538        Objects.requireNonNull(method, "method");
539        final Set<Method> result = new LinkedHashSet<>();
540        result.add(method);
541
542        final Class<?>[] parameterTypes = method.getParameterTypes();
543
544        final Class<?> declaringClass = method.getDeclaringClass();
545
546        final Iterator<Class<?>> hierarchy = ClassUtils.hierarchy(declaringClass, interfacesBehavior).iterator();
547        //skip the declaring class :P
548        hierarchy.next();
549        hierarchyTraversal: while (hierarchy.hasNext()) {
550            final Class<?> c = hierarchy.next();
551            final Method m = getMatchingAccessibleMethod(c, method.getName(), parameterTypes);
552            if (m == null) {
553                continue;
554            }
555            if (Arrays.equals(m.getParameterTypes(), parameterTypes)) {
556                // matches without generics
557                result.add(m);
558                continue;
559            }
560            // necessary to get arguments every time in the case that we are including interfaces
561            final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(declaringClass, m.getDeclaringClass());
562            for (int i = 0; i < parameterTypes.length; i++) {
563                final Type childType = TypeUtils.unrollVariables(typeArguments, method.getGenericParameterTypes()[i]);
564                final Type parentType = TypeUtils.unrollVariables(typeArguments, m.getGenericParameterTypes()[i]);
565                if (!TypeUtils.equals(childType, parentType)) {
566                    continue hierarchyTraversal;
567                }
568            }
569            result.add(m);
570        }
571        return result;
572    }
573
574    /**
575     * Gets an array of arguments in the canonical form, given an arguments array passed to a varargs method,
576     * for example an array with the declared number of parameters, and whose last parameter is an array of the varargs type.
577     *
578     * @param args the array of arguments passed to the varags method
579     * @param methodParameterTypes the declared array of method parameter types
580     * @return an array of the variadic arguments passed to the method
581     * @since 3.5
582     */
583    static Object[] getVarArgs(final Object[] args, final Class<?>[] methodParameterTypes) {
584        if (args.length == methodParameterTypes.length
585                && (args[args.length - 1] == null || args[args.length - 1].getClass().equals(methodParameterTypes[methodParameterTypes.length - 1]))) {
586            // The args array is already in the canonical form for the method.
587            return args;
588        }
589
590        // Construct a new array matching the method's declared parameter types.
591        // Copy the normal (non-varargs) parameters
592        final Object[] newArgs = ArrayUtils.arraycopy(args, 0, 0, methodParameterTypes.length - 1, () -> new Object[methodParameterTypes.length]);
593
594        // Construct a new array for the variadic parameters
595        final Class<?> varArgComponentType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
596        final int varArgLength = args.length - methodParameterTypes.length + 1;
597
598        // Copy the variadic arguments into the varargs array.
599        Object varArgsArray = ArrayUtils.arraycopy(args, methodParameterTypes.length - 1, 0, varArgLength,
600                s -> Array.newInstance(ClassUtils.primitiveToWrapper(varArgComponentType), varArgLength));
601
602        if (varArgComponentType.isPrimitive()) {
603            // unbox from wrapper type to primitive type
604            varArgsArray = ArrayUtils.toPrimitive(varArgsArray);
605        }
606
607        // Store the varargs array in the last position of the array to return
608        newArgs[methodParameterTypes.length - 1] = varArgsArray;
609
610        // Return the canonical varargs array.
611        return newArgs;
612    }
613
614    /**
615     * Invokes a method whose parameter types match exactly the object
616     * types.
617     *
618     * <p>This uses reflection to invoke the method obtained from a call to
619     * {@link #getAccessibleMethod}(Class, String, Class[])}.</p>
620     *
621     * @param object invoke method on this object
622     * @param methodName get method with this name
623     * @return The value returned by the invoked method
624     * @throws NoSuchMethodException if there is no such accessible method
625     * @throws InvocationTargetException wraps an exception thrown by the
626     *  method invoked
627     * @throws IllegalAccessException if the requested method is not accessible
628     *  via reflection
629     *
630     * @since 3.4
631     */
632    public static Object invokeExactMethod(final Object object, final String methodName) throws NoSuchMethodException,
633            IllegalAccessException, InvocationTargetException {
634        return invokeExactMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
635    }
636
637    /**
638     * Invokes a method with no parameters.
639     *
640     * <p>This uses reflection to invoke the method obtained from a call to
641     * {@link #getAccessibleMethod}(Class, String, Class[])}.</p>
642     *
643     * @param object invoke method on this object
644     * @param methodName get method with this name
645     * @param args use these arguments - treat null as empty array
646     * @return The value returned by the invoked method
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     * @throws NoSuchMethodException if there is no such accessible method
674     * @throws InvocationTargetException wraps an exception thrown by the
675     *  method invoked
676     * @throws IllegalAccessException if the requested method is not accessible
677     *  via reflection
678     * @throws NullPointerException if the object or method name are {@code null}
679     */
680    public static Object invokeExactMethod(final Object object, final String methodName, Object[] args, Class<?>[] parameterTypes)
681        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
682        Objects.requireNonNull(object, "object");
683        args = ArrayUtils.nullToEmpty(args);
684        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
685        final Class<?> cls = object.getClass();
686        final Method method = getAccessibleMethod(cls, methodName, parameterTypes);
687        if (method == null) {
688            throw new NoSuchMethodException("No such accessible method: " + methodName + "() on object: " + cls.getName());
689        }
690        return method.invoke(object, args);
691    }
692
693    /**
694     * Invokes a {@code static} method whose parameter types match exactly the object
695     * types.
696     *
697     * <p>This uses reflection to invoke the method obtained from a call to
698     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
699     *
700     * @param cls invoke static method on this class
701     * @param methodName get method with this name
702     * @param args use these arguments - treat {@code null} as empty array
703     * @return The value returned by the invoked method
704     * @throws NoSuchMethodException if there is no such accessible method
705     * @throws InvocationTargetException wraps an exception thrown by the
706     *  method invoked
707     * @throws IllegalAccessException if the requested method is not accessible
708     *  via reflection
709     */
710    public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName,
711            Object... args) throws NoSuchMethodException,
712            IllegalAccessException, InvocationTargetException {
713        args = ArrayUtils.nullToEmpty(args);
714        return invokeExactStaticMethod(cls, methodName, args, ClassUtils.toClass(args));
715    }
716
717    /**
718     * Invokes a {@code static} method whose parameter types match exactly the parameter
719     * types given.
720     *
721     * <p>This uses reflection to invoke the method obtained from a call to
722     * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
723     *
724     * @param cls invoke static method on this class
725     * @param methodName get method with this name
726     * @param args use these arguments - treat {@code null} as empty array
727     * @param parameterTypes match these parameters - treat {@code null} as empty array
728     * @return The value returned by the invoked method
729     * @throws NoSuchMethodException if there is no such accessible method
730     * @throws InvocationTargetException wraps an exception thrown by the
731     *  method invoked
732     * @throws IllegalAccessException if the requested method is not accessible
733     *  via reflection
734     */
735    public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName,
736            Object[] args, Class<?>[] parameterTypes)
737            throws NoSuchMethodException, IllegalAccessException,
738            InvocationTargetException {
739        args = ArrayUtils.nullToEmpty(args);
740        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
741        final Method method = getAccessibleMethod(cls, methodName, parameterTypes);
742        if (method == null) {
743            throw new NoSuchMethodException("No such accessible method: "
744                    + methodName + "() on class: " + cls.getName());
745        }
746        return method.invoke(null, args);
747    }
748
749    /**
750     * Invokes a named method without parameters.
751     *
752     * <p>This is a convenient wrapper for
753     * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
754     * </p>
755     *
756     * @param object invoke method on this object
757     * @param forceAccess force access to invoke method even if it's not accessible
758     * @param methodName get method with this name
759     * @return The value returned by the invoked method
760     * @throws NoSuchMethodException if there is no such accessible method
761     * @throws InvocationTargetException wraps an exception thrown by the method invoked
762     * @throws IllegalAccessException if the requested method is not accessible via reflection
763     * @throws SecurityException if an underlying accessible object's method denies the request.
764     * @see SecurityManager#checkPermission
765     * @since 3.5
766     */
767    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName)
768            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
769        return invokeMethod(object, forceAccess, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
770    }
771
772    /**
773     * Invokes a named method whose parameter type matches the object type.
774     *
775     * <p>This method supports calls to methods taking primitive parameters
776     * via passing in wrapping classes. So, for example, a {@link Boolean} object
777     * would match a {@code boolean} primitive.</p>
778     *
779     * <p>This is a convenient wrapper for
780     * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
781     * </p>
782     *
783     * @param object invoke method on this object
784     * @param forceAccess force access to invoke method even if it's not accessible
785     * @param methodName get method with this name
786     * @param args use these arguments - treat null as empty array
787     * @return The value returned by the invoked method
788     * @throws NoSuchMethodException if there is no such accessible method
789     * @throws InvocationTargetException wraps an exception thrown by the method invoked
790     * @throws IllegalAccessException if the requested method is not accessible via reflection
791     * @throws NullPointerException if the object or method name are {@code null}
792     * @throws SecurityException if an underlying accessible object's method denies the request.
793     * @see SecurityManager#checkPermission
794     * @since 3.5
795     */
796    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName,
797            Object... args) throws NoSuchMethodException,
798            IllegalAccessException, InvocationTargetException {
799        args = ArrayUtils.nullToEmpty(args);
800        return invokeMethod(object, forceAccess, methodName, args, ClassUtils.toClass(args));
801    }
802
803    /**
804     * Invokes a named method whose parameter type matches the object type.
805     *
806     * <p>This method supports calls to methods taking primitive parameters
807     * via passing in wrapping classes. So, for example, a {@link Boolean} object
808     * would match a {@code boolean} primitive.</p>
809     *
810     * @param object invoke method on this object
811     * @param forceAccess force access to invoke method even if it's not accessible
812     * @param methodName get method with this name
813     * @param args use these arguments - treat null as empty array
814     * @param parameterTypes match these parameters - treat null as empty array
815     * @return The value returned by the invoked method
816     * @throws NoSuchMethodException if there is no such accessible method
817     * @throws InvocationTargetException wraps an exception thrown by the method invoked
818     * @throws IllegalAccessException if the requested method is not accessible via reflection
819     * @throws NullPointerException if the object or method name are {@code null}
820     * @throws SecurityException if an underlying accessible object's method denies the request.
821     * @see SecurityManager#checkPermission
822     * @since 3.5
823     */
824    public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName, Object[] args, Class<?>[] parameterTypes)
825        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
826        Objects.requireNonNull(object, "object");
827        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
828        args = ArrayUtils.nullToEmpty(args);
829
830        final String messagePrefix;
831        final Method method;
832
833        final Class<? extends Object> cls = object.getClass();
834        if (forceAccess) {
835            messagePrefix = "No such method: ";
836            method = getMatchingMethod(cls, methodName, parameterTypes);
837            if (method != null && !method.isAccessible()) {
838                method.setAccessible(true);
839            }
840        } else {
841            messagePrefix = "No such accessible method: ";
842            method = getMatchingAccessibleMethod(cls, methodName, parameterTypes);
843        }
844
845        if (method == null) {
846            throw new NoSuchMethodException(messagePrefix + methodName + "() on object: " + cls.getName());
847        }
848        args = toVarArgs(method, args);
849
850        return method.invoke(object, args);
851    }
852
853    /**
854     * Invokes a named method without parameters.
855     *
856     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
857     *
858     * <p>This is a convenient wrapper for
859     * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
860     * </p>
861     *
862     * @param object invoke method on this object
863     * @param methodName get method with this name
864     * @return The value returned by the invoked method
865     * @throws NoSuchMethodException if there is no such accessible method
866     * @throws InvocationTargetException wraps an exception thrown by the method invoked
867     * @throws IllegalAccessException if the requested method is not accessible via reflection
868     * @throws SecurityException if an underlying accessible object's method denies the request.
869     * @see SecurityManager#checkPermission
870     * @since 3.4
871     */
872    public static Object invokeMethod(final Object object, final String methodName) throws NoSuchMethodException,
873            IllegalAccessException, InvocationTargetException {
874        return invokeMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
875    }
876
877    /**
878     * Invokes a named method whose parameter type matches the object type.
879     *
880     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
881     *
882     * <p>This method supports calls to methods taking primitive parameters
883     * via passing in wrapping classes. So, for example, a {@link Boolean} object
884     * would match a {@code boolean} primitive.</p>
885     *
886     * <p>This is a convenient wrapper for
887     * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
888     * </p>
889     *
890     * @param object invoke method on this object
891     * @param methodName get method with this name
892     * @param args use these arguments - treat null as empty array
893     * @return The value returned by the invoked method
894     * @throws NoSuchMethodException if there is no such accessible method
895     * @throws InvocationTargetException wraps an exception thrown by the method invoked
896     * @throws IllegalAccessException if the requested method is not accessible via reflection
897     * @throws NullPointerException if the object or method name are {@code null}
898     * @throws SecurityException if an underlying accessible object's method denies the request.
899     * @see SecurityManager#checkPermission
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     * @throws NoSuchMethodException if there is no such accessible method
923     * @throws InvocationTargetException wraps an exception thrown by the method invoked
924     * @throws IllegalAccessException if the requested method is not accessible via reflection
925     * @throws SecurityException if an underlying accessible object's method denies the request.
926     * @see SecurityManager#checkPermission
927     */
928    public static Object invokeMethod(final Object object, final String methodName,
929            final Object[] args, final Class<?>[] parameterTypes)
930            throws NoSuchMethodException, IllegalAccessException,
931            InvocationTargetException {
932        return invokeMethod(object, false, methodName, args, parameterTypes);
933    }
934
935    /**
936     * Invokes a named {@code static} method whose parameter type matches the object type.
937     *
938     * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p>
939     *
940     * <p>This method supports calls to methods taking primitive parameters
941     * via passing in wrapping classes. So, for example, a {@link Boolean} class
942     * would match a {@code boolean} primitive.</p>
943     *
944     * <p>This is a convenient wrapper for
945     * {@link #invokeStaticMethod(Class, String, Object[], Class[])}.
946     * </p>
947     *
948     * @param cls invoke static method on this class
949     * @param methodName get method with this name
950     * @param args use these arguments - treat {@code null} as empty array
951     * @return The value returned by the invoked method
952     * @throws NoSuchMethodException if there is no such accessible method
953     * @throws InvocationTargetException wraps an exception thrown by the method invoked
954     * @throws IllegalAccessException if the requested method is not accessible via reflection
955     * @throws SecurityException if an underlying accessible object's method denies the request.
956     * @see SecurityManager#checkPermission
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     * @throws NoSuchMethodException if there is no such accessible method
980     * @throws InvocationTargetException wraps an exception thrown by the method invoked
981     * @throws IllegalAccessException if the requested method is not accessible via reflection
982     * @throws SecurityException if an underlying accessible object's method denies the request.
983     * @see SecurityManager#checkPermission
984     */
985    public static Object invokeStaticMethod(final Class<?> cls, final String methodName,
986            Object[] args, Class<?>[] parameterTypes)
987            throws NoSuchMethodException, IllegalAccessException,
988            InvocationTargetException {
989        args = ArrayUtils.nullToEmpty(args);
990        parameterTypes = ArrayUtils.nullToEmpty(parameterTypes);
991        final Method method = getMatchingAccessibleMethod(cls, methodName,
992                parameterTypes);
993        if (method == null) {
994            throw new NoSuchMethodException("No such accessible method: "
995                    + methodName + "() on class: " + cls.getName());
996        }
997        args = toVarArgs(method, args);
998        return method.invoke(null, args);
999    }
1000
1001    private static Object[] toVarArgs(final Method method, Object[] args) {
1002        if (method.isVarArgs()) {
1003            final Class<?>[] methodParameterTypes = method.getParameterTypes();
1004            args = getVarArgs(args, methodParameterTypes);
1005        }
1006        return args;
1007    }
1008
1009    /**
1010     * {@link MethodUtils} instances should NOT be constructed in standard programming.
1011     * Instead, the class should be used as
1012     * {@code MethodUtils.getAccessibleMethod(method)}.
1013     *
1014     * <p>This constructor is {@code public} to permit tools that require a JavaBean
1015     * instance to operate.</p>
1016     *
1017     * @deprecated TODO Make private in 4.0.
1018     */
1019    @Deprecated
1020    public MethodUtils() {
1021        // empty
1022    }
1023}