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