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