View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.reflect;
18  
19  import java.lang.annotation.Annotation;
20  import java.lang.reflect.Array;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Type;
24  import java.lang.reflect.TypeVariable;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Comparator;
28  import java.util.Iterator;
29  import java.util.LinkedHashSet;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Objects;
33  import java.util.Set;
34  import java.util.TreeMap;
35  import java.util.stream.Collectors;
36  import java.util.stream.Stream;
37  
38  import org.apache.commons.lang3.ArrayUtils;
39  import org.apache.commons.lang3.ClassUtils;
40  import org.apache.commons.lang3.ClassUtils.Interfaces;
41  import org.apache.commons.lang3.Validate;
42  
43  /**
44   * Utility reflection methods focused on {@link Method}s, originally from Commons BeanUtils.
45   * Differences from the BeanUtils version may be noted, especially where similar functionality
46   * already existed within Lang.
47   *
48   * <h2>Known Limitations</h2>
49   * <h3>Accessing Public Methods In A Default Access Superclass</h3>
50   * <p>There is an issue when invoking {@code public} methods contained in a default access superclass on JREs prior to 1.4.
51   * Reflection locates these methods fine and correctly assigns them as {@code public}.
52   * However, an {@link IllegalAccessException} is thrown if the method is invoked.</p>
53   *
54   * <p>{@link MethodUtils} contains a workaround for this situation.
55   * It will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this method.
56   * If this call succeeds, then the method can be invoked as normal.
57   * This call will only succeed when the application has sufficient security privileges.
58   * If this call fails then the method may fail.</p>
59   *
60   * @since 2.5
61   */
62  public class MethodUtils {
63  
64      private static final Comparator<Method> METHOD_BY_SIGNATURE = Comparator.comparing(Method::toString);
65  
66      /**
67       * Returns the aggregate number of inheritance hops between assignable argument class types.  Returns -1
68       * if the arguments aren't assignable.  Fills a specific purpose for getMatchingMethod and is not generalized.
69       * @param fromClassArray the Class array to calculate the distance from.
70       * @param toClassArray the Class array to calculate the distance to.
71       * @return the aggregate number of inheritance hops between assignable argument class types.
72       */
73      private static int distance(final Class<?>[] fromClassArray, final Class<?>[] toClassArray) {
74          int answer = 0;
75  
76          if (!ClassUtils.isAssignable(fromClassArray, toClassArray, true)) {
77              return -1;
78          }
79          for (int offset = 0; offset < fromClassArray.length; offset++) {
80              // Note InheritanceUtils.distance() uses different scoring system.
81              final Class<?> aClass = fromClassArray[offset];
82              final Class<?> toClass = toClassArray[offset];
83              if (aClass == null || aClass.equals(toClass)) {
84                  continue;
85              }
86              if (ClassUtils.isAssignable(aClass, toClass, true)
87                      && !ClassUtils.isAssignable(aClass, toClass, false)) {
88                  answer++;
89              } else {
90                  answer = answer + 2;
91              }
92          }
93  
94          return answer;
95      }
96  
97      /**
98       * Returns an accessible method (that is, one that can be invoked via
99       * 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 }