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 * https://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.Executable;
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.Type;
25 import java.lang.reflect.TypeVariable;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collections;
29 import java.util.Comparator;
30 import java.util.Iterator;
31 import java.util.LinkedHashSet;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Objects;
35 import java.util.Set;
36 import java.util.TreeMap;
37 import java.util.stream.Collectors;
38 import java.util.stream.Stream;
39
40 import org.apache.commons.lang3.ArrayUtils;
41 import org.apache.commons.lang3.ClassUtils;
42 import org.apache.commons.lang3.ClassUtils.Interfaces;
43 import org.apache.commons.lang3.Validate;
44 import org.apache.commons.lang3.stream.LangCollectors;
45 import org.apache.commons.lang3.stream.Streams;
46
47 /**
48 * Utility reflection methods focused on {@link Method}s, originally from Commons BeanUtils.
49 * Differences from the BeanUtils version may be noted, especially where similar functionality
50 * already existed within Lang.
51 *
52 * <h2>Known Limitations</h2>
53 * <h3>Accessing Public Methods In A Default Access Superclass</h3>
54 * <p>
55 * There is an issue when invoking {@code public} methods contained in a default access superclass on JREs prior to 1.4.
56 * Reflection locates these methods fine and correctly assigns them as {@code public}.
57 * However, an {@link IllegalAccessException} is thrown if the method is invoked.
58 * </p>
59 *
60 * <p>
61 * {@link MethodUtils} contains a workaround for this situation.
62 * It will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this method.
63 * If this call succeeds, then the method can be invoked as normal.
64 * This call will only succeed when the application has sufficient security privileges.
65 * If this call fails then the method may fail.
66 * </p>
67 *
68 * @since 2.5
69 */
70 public class MethodUtils {
71
72 private static final Comparator<Method> METHOD_BY_SIGNATURE = Comparator.comparing(Method::toString);
73
74 /**
75 * Computes the aggregate number of inheritance hops between assignable argument class types. Returns -1
76 * if the arguments aren't assignable. Fills a specific purpose for getMatchingMethod and is not generalized.
77 *
78 * @param fromClassArray The Class array to calculate the distance from.
79 * @param toClassArray The Class array to calculate the distance to.
80 * @return The aggregate number of inheritance hops between assignable argument class types.
81 */
82 private static int distance(final Class<?>[] fromClassArray, final Class<?>[] toClassArray) {
83 int answer = 0;
84 if (!ClassUtils.isAssignable(fromClassArray, toClassArray, true)) {
85 return -1;
86 }
87 for (int offset = 0; offset < fromClassArray.length; offset++) {
88 // Note InheritanceUtils.distance() uses different scoring system.
89 final Class<?> aClass = fromClassArray[offset];
90 final Class<?> toClass = toClassArray[offset];
91 if (aClass == null || aClass.equals(toClass)) {
92 continue;
93 }
94 if (ClassUtils.isAssignable(aClass, toClass, true) && !ClassUtils.isAssignable(aClass, toClass, false)) {
95 // Autoboxing/unboxing conversion. When a primitive is boxed, rank the exact
96 // wrapper ahead of any of its supertypes so the most specific overload wins.
97 answer++;
98 if (aClass.isPrimitive() && !ClassUtils.primitiveToWrapper(aClass).equals(toClass)) {
99 answer += 2;
100 }
101 } else {
102 answer += 2;
103 }
104 }
105 return answer;
106 }
107
108 /**
109 * Gets an accessible method (that is, one that can be invoked via reflection) that implements the specified Method. If no such method can be found, return
110 * {@code null}.
111 *
112 * @param cls The implementing class, may be null.
113 * @param method The method that we wish to call, may be null.
114 * @return The accessible method or null.
115 * @since 3.19.0
116 */
117 public static Method getAccessibleMethod(final Class<?> cls, final Method method) {
118 if (!MemberUtils.isPublic(method)) {
119 return null;
120 }
121 // If the declaring class is public, we are done
122 if (ClassUtils.isPublic(cls)) {
123 return method;
124 }
125 final String methodName = method.getName();
126 final Class<?>[] parameterTypes = method.getParameterTypes();
127 // Check the implemented interfaces and subinterfaces
128 final Method method2 = getAccessibleMethodFromInterfaceNest(cls, methodName, parameterTypes);
129 // Check the superclass chain
130 return method2 != null ? method2 : getAccessibleMethodFromSuperclass(cls, methodName, parameterTypes);
131 }
132
133 /**
134 * Gets an accessible method (that is, one that can be invoked via reflection) with given name and parameters. If no such method can be found, return
135 * {@code null}. This is just a convenience wrapper for {@link #getAccessibleMethod(Method)}.
136 *
137 * @param cls get method from this class.
138 * @param methodName get method with this name.
139 * @param parameterTypes with these parameters types.
140 * @return The accessible method.
141 */
142 public static Method getAccessibleMethod(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
143 return getAccessibleMethod(getMethodObject(cls, methodName, parameterTypes));
144 }
145
146 /**
147 * Gets an accessible method (that is, one that can be invoked via reflection) that implements the specified Method. If no such method can be found, return
148 * {@code null}.
149 *
150 * @param method The method that we wish to call, may be null.
151 * @return The accessible method
152 */
153 public static Method getAccessibleMethod(final Method method) {
154 return method != null ? getAccessibleMethod(method.getDeclaringClass(), method) : null;
155 }
156
157 /**
158 * Gets an accessible method (that is, one that can be invoked via
159 * reflection) that implements the specified method, by scanning through
160 * all implemented interfaces and subinterfaces. If no such method
161 * can be found, return {@code null}.
162 *
163 * <p>
164 * There isn't any good reason why this method must be {@code private}.
165 * It is because there doesn't seem any reason why other classes should
166 * call this rather than the higher level methods.
167 * </p>
168 *
169 * @param cls Parent class for the interfaces to be checked.
170 * @param methodName Method name of the method we wish to call.
171 * @param parameterTypes The parameter type signatures.
172 * @return The accessible method or {@code null} if not found.
173 */
174 private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
175 // Search up the superclass chain
176 for (; cls != null; cls = cls.getSuperclass()) {
177 // Check the implemented interfaces of the parent class
178 final Class<?>[] interfaces = cls.getInterfaces();
179 for (final Class<?> anInterface : interfaces) {
180 // Is this interface public?
181 if (!ClassUtils.isPublic(anInterface)) {
182 continue;
183 }
184 // Does the method exist on this interface? A static or private one is not inherited.
185 try {
186 final Method declared = anInterface.getDeclaredMethod(methodName, parameterTypes);
187 if (MemberUtils.isPublic(declared) && !MemberUtils.isStatic(declared)) {
188 return declared;
189 }
190 } catch (final NoSuchMethodException ignored) {
191 /*
192 * Swallow, if no method is found after the loop then this method returns null.
193 */
194 }
195 // Recursively check our parent interfaces
196 final Method method = getAccessibleMethodFromInterfaceNest(anInterface, methodName, parameterTypes);
197 if (method != null) {
198 return method;
199 }
200 }
201 }
202 return null;
203 }
204
205 /**
206 * Gets an accessible method (that is, one that can be invoked via
207 * reflection) by scanning through the superclasses. If no such method
208 * can be found, return {@code null}.
209 *
210 * @param cls Class to be checked.
211 * @param methodName Method name of the method we wish to call.
212 * @param parameterTypes The parameter type signatures.
213 * @return The accessible method or {@code null} if not found.
214 */
215 private static Method getAccessibleMethodFromSuperclass(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
216 Class<?> parentClass = cls.getSuperclass();
217 while (parentClass != null) {
218 if (ClassUtils.isPublic(parentClass)) {
219 return getMethodObject(parentClass, methodName, parameterTypes);
220 }
221 parentClass = parentClass.getSuperclass();
222 }
223 return null;
224 }
225
226 /**
227 * Gets a combination of {@link ClassUtils#getAllSuperclasses(Class)} and {@link ClassUtils#getAllInterfaces(Class)}, one from superclasses, one from
228 * interfaces, and so on in a breadth first way.
229 *
230 * @param cls The class to look up, may be {@code null}.
231 * @return The combined {@link List} of superclasses and interfaces in order going up from this one {@code null} if null input.
232 */
233 private static List<Class<?>> getAllSuperclassesAndInterfaces(final Class<?> cls) {
234 if (cls == null) {
235 return null;
236 }
237 final List<Class<?>> allSuperClassesAndInterfaces = new ArrayList<>();
238 final List<Class<?>> allSuperclasses = ClassUtils.getAllSuperclasses(cls);
239 int superClassIndex = 0;
240 final List<Class<?>> allInterfaces = ClassUtils.getAllInterfaces(cls);
241 int interfaceIndex = 0;
242 while (interfaceIndex < allInterfaces.size() || superClassIndex < allSuperclasses.size()) {
243 final Class<?> acls;
244 if (interfaceIndex >= allInterfaces.size() || superClassIndex < allSuperclasses.size() && superClassIndex < interfaceIndex) {
245 acls = allSuperclasses.get(superClassIndex++);
246 } else {
247 acls = allInterfaces.get(interfaceIndex++);
248 }
249 allSuperClassesAndInterfaces.add(acls);
250 }
251 return allSuperClassesAndInterfaces;
252 }
253
254 /**
255 * Gets the annotation object with the given annotation type that is present on the given method or optionally on any equivalent method in super classes and
256 * interfaces. Returns null if the annotation type was not present.
257 *
258 * <p>
259 * Stops searching for an annotation once the first annotation of the specified type has been found. Additional annotations of the specified type will be
260 * silently ignored.
261 * </p>
262 *
263 * @param <A> the annotation type.
264 * @param method The {@link Method} to query, may be null.
265 * @param annotationCls The {@link Annotation} to check if is present on the method.
266 * @param searchSupers determines if a lookup in the entire inheritance hierarchy of the given class is performed if the annotation was not directly
267 * present.
268 * @param ignoreAccess determines if underlying method has to be accessible.
269 * @return The first matching annotation, or {@code null} if not found.
270 * @throws NullPointerException Thrown if either the method or annotation class is {@code null}.
271 * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
272 * @see SecurityManager#checkPermission
273 * @since 3.6
274 */
275 public static <A extends Annotation> A getAnnotation(final Method method, final Class<A> annotationCls, final boolean searchSupers,
276 final boolean ignoreAccess) {
277 Objects.requireNonNull(method, "method");
278 Objects.requireNonNull(annotationCls, "annotationCls");
279 if (!ignoreAccess && !MemberUtils.isAccessible(method)) {
280 return null;
281 }
282 A annotation = method.getAnnotation(annotationCls);
283 if (annotation == null && searchSupers) {
284 final Class<?> mcls = method.getDeclaringClass();
285 final String methodName = method.getName();
286 final Class<?>[] paramTypes = method.getParameterTypes();
287 final List<Class<?>> classes = getAllSuperclassesAndInterfaces(mcls);
288 for (final Class<?> acls : classes) {
289 // First, attempt an exact parameter-type match (getDeclaredMethod) to
290 // find a true override. This avoids matching unrelated overloads that
291 // are merely assignable-compatible (e.g. process(Integer) vs
292 // process(Number)).
293 Method equivalentMethod = null;
294 try {
295 equivalentMethod = acls.getDeclaredMethod(methodName, paramTypes);
296 } catch (final NoSuchMethodException ignored) {
297 // No exact match; check for generic-bridge scenario: the declaring
298 // class may use a type variable whose erased form is Object (or
299 // another bound). In that case the parent method's erased
300 // parameter types differ from the child's concrete types, so we
301 // scan declared methods for a same-name method whose *erased*
302 // parameter count matches and whose erased types are assignable
303 // from our concrete types.
304 for (final Method candidate : acls.getDeclaredMethods()) {
305 if (!candidate.getName().equals(methodName)) {
306 continue;
307 }
308 final Class<?>[] candidateParams = candidate.getParameterTypes();
309 if (candidateParams.length != paramTypes.length) {
310 continue;
311 }
312 // Require that every concrete param type is assignable to the
313 // candidate's (erased) param type AND that the candidate is
314 // generic (has at least one TypeVariable in its generic
315 // parameter types). This prevents matching plain overloads.
316 boolean genericMatch = false;
317 boolean paramsMatch = true;
318 final java.lang.reflect.Type[] genericParams = candidate.getGenericParameterTypes();
319 for (int i = 0; i < candidateParams.length; i++) {
320 if (genericParams[i] instanceof java.lang.reflect.TypeVariable) {
321 genericMatch = true;
322 }
323 if (!ClassUtils.isAssignable(paramTypes[i], candidateParams[i], true)) {
324 paramsMatch = false;
325 break;
326 }
327 }
328 if (paramsMatch && genericMatch) {
329 equivalentMethod = candidate;
330 break;
331 }
332 }
333 }
334 if (equivalentMethod != null && (ignoreAccess || MemberUtils.isAccessible(equivalentMethod))) {
335 annotation = equivalentMethod.getAnnotation(annotationCls);
336 if (annotation != null) {
337 break;
338 }
339 }
340 }
341 }
342 return annotation;
343 }
344
345 private static Method getInvokeMethod(final boolean forceAccess, final String methodName, final Class<?>[] parameterTypes, final Class<?> cls) {
346 final Method method;
347 if (forceAccess) {
348 method = getMatchingMethod(cls, methodName, parameterTypes);
349 AccessibleObjects.setAccessible(method);
350 } else {
351 method = getMatchingAccessibleMethod(cls, methodName, parameterTypes);
352 }
353 return method;
354 }
355
356 /**
357 * Gets an accessible method that matches the given name and has compatible parameters. Compatible parameters mean that every method parameter is assignable
358 * from the given parameters. In other words, it finds a method with the given name that will take the parameters given.
359 *
360 * <p>
361 * This method is used by {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
362 * </p>
363 * <p>
364 * This method can match primitive parameter by passing in wrapper classes. For example, a {@link Boolean} will match a primitive {@code boolean} parameter.
365 * </p>
366 *
367 * @param cls find method in this class.
368 * @param methodName find method with this name.
369 * @param requestTypes find method with most compatible parameters.
370 * @return The accessible method or null.
371 * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
372 * @see SecurityManager#checkPermission
373 */
374 public static Method getMatchingAccessibleMethod(final Class<?> cls, final String methodName, final Class<?>... requestTypes) {
375 final Method candidate = getMethodObject(cls, methodName, requestTypes);
376 if (candidate != null) {
377 // The exact match may be declared on a non-public class, so prefer the public
378 // declaration the way the search below does; a static method hides, so it is kept.
379 final Method accessibleCandidate = MemberUtils.isStatic(candidate) ? null : getAccessibleMethod(cls, candidate);
380 return MemberUtils.setAccessibleWorkaround(accessibleCandidate != null ? accessibleCandidate : candidate);
381 }
382 // search through all methods
383 final Method[] methods = cls.getMethods();
384 final List<Method> matchingMethods = Stream.of(methods)
385 .filter(method -> method.getName().equals(methodName) && MemberUtils.isMatchingMethod(method, requestTypes)).collect(Collectors.toList());
386 // Sort methods by signature to force deterministic result
387 matchingMethods.sort(METHOD_BY_SIGNATURE);
388 Method bestMatch = null;
389 for (final Method method : matchingMethods) {
390 // get accessible version of method
391 final Method accessibleMethod = getAccessibleMethod(method);
392 if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareMethodFit(accessibleMethod, bestMatch, requestTypes) < 0)) {
393 bestMatch = accessibleMethod;
394 }
395 }
396 if (bestMatch != null) {
397 MemberUtils.setAccessibleWorkaround(bestMatch);
398 if (bestMatch.isVarArgs()) {
399 final Class<?>[] bestMatchParameterTypes = bestMatch.getParameterTypes();
400 final Class<?> varArgType = bestMatchParameterTypes[bestMatchParameterTypes.length - 1].getComponentType();
401 for (int paramIdx = bestMatchParameterTypes.length - 1; paramIdx < requestTypes.length; paramIdx++) {
402 final Class<?> parameterType = requestTypes[paramIdx];
403 if (!ClassUtils.isAssignable(parameterType, varArgType, true)) {
404 return null;
405 }
406 }
407 }
408 }
409 return bestMatch;
410 }
411
412 /**
413 * Gets a method whether or not it's accessible. If no such method can be found, return {@code null}.
414 *
415 * @param cls The class that will be subjected to the method search.
416 * @param methodName The method that we wish to call.
417 * @param parameterTypes Argument class types.
418 * @throws IllegalStateException Thrown if there is no unique result.
419 * @throws NullPointerException Thrown if the class is {@code null}.
420 * @return The method.
421 * @since 3.5
422 */
423 public static Method getMatchingMethod(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) {
424 Objects.requireNonNull(cls, "cls");
425 Validate.notEmpty(methodName, "methodName");
426 final List<Method> methods = Stream.of(cls.getDeclaredMethods())
427 .filter(method -> method.getName().equals(methodName))
428 .collect(Collectors.toList());
429 final List<Class<?>> allSuperclassesAndInterfaces = getAllSuperclassesAndInterfaces(cls);
430 Collections.reverse(allSuperclassesAndInterfaces);
431 allSuperclassesAndInterfaces.stream()
432 .map(Class::getDeclaredMethods)
433 .flatMap(Stream::of)
434 .filter(method -> method.getName().equals(methodName))
435 .forEach(methods::add);
436 for (final Method method : methods) {
437 if (Arrays.deepEquals(method.getParameterTypes(), parameterTypes)) {
438 return method;
439 }
440 }
441 final TreeMap<Integer, List<Method>> candidates = new TreeMap<>();
442 methods.stream()
443 .filter(method -> ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true))
444 .forEach(method -> {
445 final int distance = distance(parameterTypes, method.getParameterTypes());
446 final List<Method> candidatesAtDistance = candidates.computeIfAbsent(distance, k -> new ArrayList<>());
447 candidatesAtDistance.add(method);
448 });
449 if (candidates.isEmpty()) {
450 return null;
451 }
452 final List<Method> bestCandidates = candidates.values().iterator().next();
453 if (bestCandidates.size() == 1 || !Objects.equals(bestCandidates.get(0).getDeclaringClass(),
454 bestCandidates.get(1).getDeclaringClass())) {
455 return bestCandidates.get(0);
456 }
457 throw new IllegalStateException(String.format("Found multiple candidates for method %s on class %s : %s",
458 methodName + Stream.of(parameterTypes).map(String::valueOf).collect(Collectors.joining(",", "(", ")")), cls.getName(),
459 bestCandidates.stream().map(Method::toString).collect(Collectors.joining(",", "[", "]"))));
460 }
461
462 /**
463 * Gets a Method, or {@code null} if a checked {@link Class#getMethod(String, Class...) } exception is thrown.
464 *
465 * @param cls Receiver for {@link Class#getMethod(String, Class...)}.
466 * @param name The name of the method.
467 * @param parameterTypes The list of parameters.
468 * @return A Method or {@code null}.
469 * @see SecurityManager#checkPermission
470 * @see Class#getMethod(String, Class...)
471 * @since 3.15.0
472 */
473 public static Method getMethodObject(final Class<?> cls, final String name, final Class<?>... parameterTypes) {
474 try {
475 return name != null && cls != null ? cls.getMethod(name, parameterTypes) : null;
476 } catch (final NoSuchMethodException | SecurityException e) {
477 return null;
478 }
479 }
480
481 /**
482 * Gets all class level public methods of the given class that are annotated with the given annotation.
483 *
484 * @param cls The {@link Class} to query.
485 * @param annotationCls The {@link Annotation} that must be present on a method to be matched.
486 * @return A list of Methods (possibly empty).
487 * @throws NullPointerException Thrown if the class or annotation are {@code null}.
488 * @since 3.4
489 */
490 public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
491 return getMethodsListWithAnnotation(cls, annotationCls, false, false);
492 }
493
494 /**
495 * Gets all methods of the given class that are annotated with the given annotation.
496 *
497 * @param cls The {@link Class} to query.
498 * @param annotationCls The {@link Annotation} that must be present on a method to be matched.
499 * @param searchSupers determines if a lookup in the entire inheritance hierarchy of the given class should be performed.
500 * @param ignoreAccess determines if non-public methods should be considered.
501 * @return A list of Methods (possibly empty).
502 * @throws NullPointerException Thrown if either the class or annotation class is {@code null}.
503 * @since 3.6
504 */
505 public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls, final boolean searchSupers,
506 final boolean ignoreAccess) {
507 Objects.requireNonNull(cls, "cls");
508 Objects.requireNonNull(annotationCls, "annotationCls");
509 final List<Class<?>> classes = searchSupers ? getAllSuperclassesAndInterfaces(cls) : new ArrayList<>();
510 classes.add(0, cls);
511 final List<Method> annotatedMethods = new ArrayList<>();
512 classes.forEach(acls -> {
513 final Method[] methods = ignoreAccess ? acls.getDeclaredMethods() : acls.getMethods();
514 Stream.of(methods).filter(method -> method.isAnnotationPresent(annotationCls)).forEachOrdered(annotatedMethods::add);
515 });
516 return annotatedMethods;
517 }
518
519 /**
520 * Gets all class level public methods of the given class that are annotated with the given annotation.
521 *
522 * @param cls The {@link Class} to query.
523 * @param annotationCls The {@link java.lang.annotation.Annotation} that must be present on a method to be matched.
524 * @return An array of Methods (possibly empty).
525 * @throws NullPointerException Thrown if the class or annotation are {@code null}.
526 * @since 3.4
527 */
528 public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
529 return getMethodsWithAnnotation(cls, annotationCls, false, false);
530 }
531
532 /**
533 * Gets all methods of the given class that are annotated with the given annotation.
534 *
535 * @param cls The {@link Class} to query.
536 * @param annotationCls The {@link java.lang.annotation.Annotation} that must be present on a method to be matched.
537 * @param searchSupers determines if a lookup in the entire inheritance hierarchy of the given class should be performed.
538 * @param ignoreAccess determines if non-public methods should be considered.
539 * @return An array of Methods (possibly empty).
540 * @throws NullPointerException Thrown if the class or annotation are {@code null}.
541 * @since 3.6
542 */
543 public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls, final boolean searchSupers,
544 final boolean ignoreAccess) {
545 return getMethodsListWithAnnotation(cls, annotationCls, searchSupers, ignoreAccess).toArray(ArrayUtils.EMPTY_METHOD_ARRAY);
546 }
547
548 /**
549 * Gets the hierarchy of overridden methods down to {@code result} respecting generics.
550 *
551 * @param method lowest to consider.
552 * @param interfacesBehavior whether to search interfaces, {@code null} {@code implies} false.
553 * @return A {@code Set<Method>} in ascending order from subclass to superclass.
554 * @throws NullPointerException Thrown if the specified method is {@code null}.
555 * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
556 * @see SecurityManager#checkPermission
557 * @since 3.2
558 */
559 public static Set<Method> getOverrideHierarchy(final Method method, final Interfaces interfacesBehavior) {
560 Objects.requireNonNull(method, "method");
561 final Set<Method> result = new LinkedHashSet<>();
562 result.add(method);
563 final Class<?>[] parameterTypes = method.getParameterTypes();
564 final Class<?> declaringClass = method.getDeclaringClass();
565 final Iterator<Class<?>> hierarchy = ClassUtils.hierarchy(declaringClass, interfacesBehavior).iterator();
566 //skip the declaring class :P
567 hierarchy.next();
568 hierarchyTraversal: while (hierarchy.hasNext()) {
569 final Class<?> c = hierarchy.next();
570 final Method m = getMatchingAccessibleMethod(c, method.getName(), parameterTypes);
571 if (m == null) {
572 continue;
573 }
574 if (Arrays.equals(m.getParameterTypes(), parameterTypes)) {
575 // matches without generics
576 result.add(m);
577 continue;
578 }
579 // necessary to get arguments every time in the case that we are including interfaces
580 final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(declaringClass, m.getDeclaringClass());
581 for (int i = 0; i < parameterTypes.length; i++) {
582 final Type childType = TypeUtils.unrollVariables(typeArguments, method.getGenericParameterTypes()[i]);
583 final Type parentType = TypeUtils.unrollVariables(typeArguments, m.getGenericParameterTypes()[i]);
584 if (!TypeUtils.equals(childType, parentType)) {
585 continue hierarchyTraversal;
586 }
587 }
588 result.add(m);
589 }
590 return result;
591 }
592
593 /**
594 * Invokes a method whose parameter types match exactly the object type.
595 *
596 * <p>
597 * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
598 * </p>
599 *
600 * @param object invoke method on this object.
601 * @param methodName get method with this name.
602 * @return The value returned by the invoked method.
603 * @throws NoSuchMethodException Thrown if there is no such accessible method.
604 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
605 * inaccessible.
606 * @throws IllegalArgumentException Thrown if:
607 * <ul>
608 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
609 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
610 * <li>the number of actual and formal parameters differ;</li>
611 * </ul>
612 * @throws InvocationTargetException Thrown if the underlying method throws an exception.
613 * @throws NullPointerException Thrown if the specified {@code object} is null.
614 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
615 * @since 3.4
616 */
617 public static Object invokeExactMethod(final Object object, final String methodName)
618 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
619 return invokeExactMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
620 }
621
622 /**
623 * Invokes a method whose parameter types match exactly the object types.
624 *
625 * <p>
626 * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
627 * </p>
628 *
629 * @param object invoke method on this object.
630 * @param methodName get method with this name.
631 * @param args use these arguments - treat null as empty array.
632 * @return The value returned by the invoked method.
633 * @throws NoSuchMethodException Thrown if there is no such accessible method.
634 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
635 * inaccessible.
636 * @throws IllegalArgumentException Thrown if:
637 * <ul>
638 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
639 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
640 * <li>the number of actual and formal parameters differ;</li>
641 * <li>an unwrapping conversion for primitive arguments fails; or</li>
642 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
643 * method invocation conversion.</li>
644 * </ul>
645 * @throws InvocationTargetException Thrown if the underlying method throws an exception.
646 * @throws NullPointerException Thrown if the specified {@code object} is null.
647 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
648 */
649 public static Object invokeExactMethod(final Object object, final String methodName, final Object... args)
650 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
651 final Object[] actuals = ArrayUtils.nullToEmpty(args);
652 return invokeExactMethod(object, methodName, actuals, ClassUtils.toClass(actuals));
653 }
654
655 /**
656 * Invokes a method whose parameter types match exactly the parameter types given.
657 *
658 * <p>
659 * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
660 * </p>
661 *
662 * @param object Invokes a method on this object.
663 * @param methodName Gets a method with this name.
664 * @param args Method arguments - treat null as empty array.
665 * @param parameterTypes Match these parameters - treat {@code null} as empty array.
666 * @return The value returned by the invoked method.
667 * @throws NoSuchMethodException Thrown if there is no such accessible method.
668 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
669 * inaccessible.
670 * @throws IllegalArgumentException Thrown if:
671 * <ul>
672 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
673 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
674 * <li>the number of actual and formal parameters differ;</li>
675 * <li>an unwrapping conversion for primitive arguments fails; or</li>
676 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
677 * method invocation conversion.</li>
678 * </ul>
679 * @throws InvocationTargetException Thrown if the underlying method throws an exception.
680 * @throws NullPointerException Thrown if the specified {@code object} is null.
681 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
682 */
683 public static Object invokeExactMethod(final Object object, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
684 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
685 final Class<?> cls = Objects.requireNonNull(object, "object").getClass();
686 final Class<?>[] paramTypes = ArrayUtils.nullToEmpty(parameterTypes);
687 final Method method = getAccessibleMethod(cls, methodName, paramTypes);
688 requireNonNull(method, cls, methodName, paramTypes);
689 return method.invoke(object, ArrayUtils.nullToEmpty(args));
690 }
691
692 /**
693 * Invokes a {@code static} method whose parameter types match exactly the object types.
694 *
695 * <p>
696 * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
697 * </p>
698 *
699 * @param cls invoke static method on this class.
700 * @param methodName get method with this name.
701 * @param args use these arguments - treat {@code null} as empty array.
702 * @return The value returned by the invoked method.
703 * @throws NoSuchMethodException Thrown if there is no such accessible method.
704 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
705 * inaccessible.
706 * @throws IllegalArgumentException Thrown if:
707 * <ul>
708 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
709 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
710 * <li>the number of actual and formal parameters differ;</li>
711 * <li>an unwrapping conversion for primitive arguments fails; or</li>
712 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
713 * method invocation conversion.</li>
714 * </ul>
715 * @throws InvocationTargetException Thrown if the underlying method throws an exception.
716 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
717 */
718 public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName, final Object... args)
719 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
720 final Object[] actuals = ArrayUtils.nullToEmpty(args);
721 return invokeExactStaticMethod(cls, methodName, actuals, ClassUtils.toClass(actuals));
722 }
723
724 /**
725 * Invokes a {@code static} method whose parameter types match exactly the parameter types given.
726 *
727 * <p>
728 * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
729 * </p>
730 *
731 * @param cls invoke static method on this class.
732 * @param methodName get method with this name.
733 * @param args use these arguments - treat {@code null} as empty array.
734 * @param parameterTypes match these parameters - treat {@code null} as empty array.
735 * @return The value returned by the invoked method.
736 * @throws NoSuchMethodException Thrown if there is no such accessible method.
737 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
738 * inaccessible.
739 * @throws IllegalArgumentException Thrown if:
740 * <ul>
741 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
742 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
743 * <li>the number of actual and formal parameters differ;</li>
744 * <li>an unwrapping conversion for primitive arguments fails; or</li>
745 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
746 * method invocation conversion.</li>
747 * </ul>
748 * @throws InvocationTargetException Thrown if the underlying method throws an exception.
749 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
750 */
751 public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
752 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
753 final Class<?>[] paramTypes = ArrayUtils.nullToEmpty(parameterTypes);
754 final Method method = getAccessibleMethod(cls, methodName, ArrayUtils.nullToEmpty(paramTypes));
755 requireNonNull(method, cls, methodName, paramTypes);
756 return method.invoke(null, ArrayUtils.nullToEmpty(args));
757 }
758
759 /**
760 * Invokes a named method without parameters.
761 *
762 * <p>
763 * This is a convenient wrapper for
764 * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
765 * </p>
766 *
767 * @param object invoke method on this object.
768 * @param forceAccess force access to invoke method even if it's not accessible.
769 * @param methodName get method with this name.
770 * @return The value returned by the invoked method.
771 * @throws NoSuchMethodException Thrown if there is no such accessible method.
772 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
773 * inaccessible.
774 * @throws IllegalArgumentException Thrown if:
775 * <ul>
776 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
777 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
778 * <li>the number of actual and formal parameters differ;</li>
779 * <li>an unwrapping conversion for primitive arguments fails; or</li>
780 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
781 * method invocation conversion.</li>
782 * </ul>
783 * @throws InvocationTargetException Thrown if the underlying method throws an exception.
784 * @throws NullPointerException Thrown if the specified {@code object} is null.
785 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
786 * @see SecurityManager#checkPermission
787 * @since 3.5
788 */
789 public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName)
790 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
791 return invokeMethod(object, forceAccess, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
792 }
793
794 /**
795 * Invokes a named method whose parameter type matches the object type.
796 *
797 * <p>
798 * This method supports calls to methods taking primitive parameters
799 * via passing in wrapping classes. So, for example, a {@link Boolean} object
800 * would match a {@code boolean} primitive.
801 * </p>
802 * <p>
803 * This is a convenient wrapper for
804 * {@link #invokeMethod(Object object, boolean forceAccess, String methodName, Object[] args, Class[] parameterTypes)}.
805 * </p>
806 *
807 * @param object invoke method on this object.
808 * @param forceAccess force access to invoke method even if it's not accessible.
809 * @param methodName get method with this name.
810 * @param args use these arguments - treat null as empty array.
811 * @return The value returned by the invoked method.
812 * @throws NoSuchMethodException Thrown if there is no such accessible method.
813 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
814 * inaccessible.
815 * @throws IllegalArgumentException Thrown if:
816 * <ul>
817 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
818 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
819 * <li>the number of actual and formal parameters differ;</li>
820 * <li>an unwrapping conversion for primitive arguments fails; or</li>
821 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
822 * method invocation conversion.</li>
823 * </ul>
824 * @throws InvocationTargetException Thrown if the underlying method throws an exception.
825 * @throws NullPointerException Thrown if the specified {@code object} is null.
826 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
827 * @see SecurityManager#checkPermission
828 * @since 3.5
829 */
830 public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName, final Object... args)
831 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
832 final Object[] actuals = ArrayUtils.nullToEmpty(args);
833 return invokeMethod(object, forceAccess, methodName, actuals, ClassUtils.toClass(actuals));
834 }
835
836 /**
837 * Invokes a named method whose parameter type matches the object type.
838 *
839 * <p>
840 * This method supports calls to methods taking primitive parameters
841 * via passing in wrapping classes. So, for example, a {@link Boolean} object
842 * would match a {@code boolean} primitive.
843 * </p>
844 *
845 * @param object invoke method on this object.
846 * @param forceAccess force access to invoke method even if it's not accessible.
847 * @param methodName get method with this name.
848 * @param args use these arguments - treat null as empty array.
849 * @param parameterTypes match these parameters - treat null as empty array.
850 * @return The value returned by the invoked method.
851 * @throws NoSuchMethodException Thrown if there is no such accessible method.
852 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
853 * inaccessible.
854 * @throws IllegalArgumentException Thrown if:
855 * <ul>
856 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
857 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
858 * <li>the number of actual and formal parameters differ;</li>
859 * <li>an unwrapping conversion for primitive arguments fails; or</li>
860 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
861 * method invocation conversion.</li>
862 * </ul>
863 * @throws InvocationTargetException Thrown if the underlying method throws an exception.
864 * @throws NullPointerException Thrown if the specified {@code object} is null.
865 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
866 * @see SecurityManager#checkPermission
867 * @since 3.5
868 */
869 public static Object invokeMethod(final Object object, final boolean forceAccess, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
870 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
871 final Class<?> cls = Objects.requireNonNull(object, "object").getClass();
872 final Class<?>[] paramTypes = ArrayUtils.nullToEmpty(parameterTypes);
873 final Method method = getInvokeMethod(forceAccess, methodName, paramTypes, cls);
874 requireNonNull(method, cls, methodName, paramTypes);
875 return method.invoke(object, toVarArgs(method, ArrayUtils.nullToEmpty(args)));
876 }
877
878 /**
879 * Invokes a named method without parameters.
880 *
881 * <p>
882 * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
883 * </p>
884 * <p>
885 * This is a convenient wrapper for
886 * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
887 * </p>
888 *
889 * @param object invoke method on this object.
890 * @param methodName get method with this name.
891 * @return The value returned by the invoked method.
892 * @throws NoSuchMethodException Thrown if there is no such accessible method.
893 * @throws InvocationTargetException Thrown to wrap an exception thrown by the method invoked.
894 * @throws IllegalAccessException Thrown if the requested method is not accessible via reflection.
895 * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
896 * @see SecurityManager#checkPermission
897 * @since 3.4
898 */
899 public static Object invokeMethod(final Object object, final String methodName) throws NoSuchMethodException,
900 IllegalAccessException, InvocationTargetException {
901 return invokeMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null);
902 }
903
904 /**
905 * Invokes a named method whose parameter type matches the object type.
906 *
907 * <p>
908 * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
909 * </p>
910 * <p>
911 * This method supports calls to methods taking primitive parameters
912 * via passing in wrapping classes. So, for example, a {@link Boolean} object
913 * would match a {@code boolean} primitive.
914 * </p>
915 * <p>
916 * This is a convenient wrapper for
917 * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
918 * </p>
919 *
920 * @param object invoke method on this object.
921 * @param methodName get method with this name.
922 * @param args use these arguments - treat null as empty array.
923 * @return The value returned by the invoked method.
924 * @throws NoSuchMethodException Thrown if there is no such accessible method.
925 * @throws InvocationTargetException Thrown to wrap an exception thrown by the method invoked.
926 * @throws IllegalAccessException Thrown if the requested method is not accessible via reflection.
927 * @throws NullPointerException Thrown if the object or method name are {@code null}.
928 * @throws SecurityException Thrown if an underlying accessible object's method denies the request.
929 * @see SecurityManager#checkPermission
930 */
931 public static Object invokeMethod(final Object object, final String methodName, final Object... args)
932 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
933 final Object[] actuals = ArrayUtils.nullToEmpty(args);
934 return invokeMethod(object, methodName, actuals, ClassUtils.toClass(actuals));
935 }
936
937 /**
938 * Invokes a named method whose parameter type matches the object type.
939 *
940 * <p>
941 * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
942 * </p>
943 * <p>
944 * This method supports calls to methods taking primitive parameters
945 * via passing in wrapping classes. So, for example, a {@link Boolean} object
946 * would match a {@code boolean} primitive.
947 * </p>
948 *
949 * @param object invoke method on this object.
950 * @param methodName get method with this name.
951 * @param args use these arguments - treat null as empty array.
952 * @param parameterTypes match these parameters - treat null as empty array.
953 * @return The value returned by the invoked method.
954 * @throws NoSuchMethodException Thrown if there is no such accessible method.
955 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
956 * inaccessible.
957 * @throws IllegalArgumentException Thrown if:
958 * <ul>
959 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
960 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
961 * <li>the number of actual and formal parameters differ;</li>
962 * <li>an unwrapping conversion for primitive arguments fails; or</li>
963 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
964 * method invocation conversion.</li>
965 * </ul>
966 * @throws InvocationTargetException Thrown if the underlying method throws an exception.
967 * @throws NullPointerException Thrown if the specified {@code object} is null.
968 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
969 * @see SecurityManager#checkPermission
970 */
971 public static Object invokeMethod(final Object object, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
972 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
973 return invokeMethod(object, false, methodName, args, parameterTypes);
974 }
975
976 /**
977 * Invokes a named {@code static} method whose parameter type matches the object type.
978 *
979 * <p>
980 * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
981 * </p>
982 * <p>
983 * This method supports calls to methods taking primitive parameters
984 * via passing in wrapping classes. So, for example, a {@link Boolean} class
985 * would match a {@code boolean} primitive.
986 * </p>
987 * <p>
988 * This is a convenient wrapper for
989 * {@link #invokeStaticMethod(Class, String, Object[], Class[])}.
990 * </p>
991 *
992 * @param cls invoke static method on this class.
993 * @param methodName get method with this name.
994 * @param args use these arguments - treat {@code null} as empty array.
995 * @return The value returned by the invoked method.
996 * @throws NoSuchMethodException Thrown if there is no such accessible method.
997 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
998 * inaccessible.
999 * @throws IllegalArgumentException Thrown if:
1000 * <ul>
1001 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
1002 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
1003 * <li>the number of actual and formal parameters differ;</li>
1004 * <li>an unwrapping conversion for primitive arguments fails; or</li>
1005 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
1006 * method invocation conversion.</li>
1007 * </ul>
1008 * @throws InvocationTargetException Thrown if the underlying method throws an exception.
1009 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
1010 * @see SecurityManager#checkPermission
1011 */
1012 public static Object invokeStaticMethod(final Class<?> cls, final String methodName, final Object... args)
1013 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
1014 final Object[] actuals = ArrayUtils.nullToEmpty(args);
1015 return invokeStaticMethod(cls, methodName, actuals, ClassUtils.toClass(actuals));
1016 }
1017
1018 /**
1019 * Invokes a named {@code static} method whose parameter type matches the object type.
1020 *
1021 * <p>
1022 * This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
1023 * </p>
1024 * <p>
1025 * This method supports calls to methods taking primitive parameters
1026 * via passing in wrapping classes. So, for example, a {@link Boolean} class
1027 * would match a {@code boolean} primitive.
1028 * </p>
1029 *
1030 * @param cls invoke static method on this class.
1031 * @param methodName get method with this name.
1032 * @param args use these arguments - treat {@code null} as empty array.
1033 * @param parameterTypes match these parameters - treat {@code null} as empty array.
1034 * @return The value returned by the invoked method.
1035 * @throws NoSuchMethodException Thrown if there is no such accessible method.
1036 * @throws IllegalAccessException Thrown if this found {@code Method} is enforcing Java language access control and the underlying method is
1037 * inaccessible.
1038 * @throws IllegalArgumentException Thrown if:
1039 * <ul>
1040 * <li>the found {@code Method} is an instance method and the specified {@code object} argument is not an instance of
1041 * the class or interface declaring the underlying method (or of a subclass or interface implementor);</li>
1042 * <li>the number of actual and formal parameters differ;</li>
1043 * <li>an unwrapping conversion for primitive arguments fails; or</li>
1044 * <li>after possible unwrapping, a parameter value can't be converted to the corresponding formal parameter type by a
1045 * method invocation conversion.</li>
1046 * </ul>
1047 * @throws InvocationTargetException Thrown if the underlying method throws an exception.
1048 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
1049 * @see SecurityManager#checkPermission
1050 */
1051 public static Object invokeStaticMethod(final Class<?> cls, final String methodName, final Object[] args, final Class<?>[] parameterTypes)
1052 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
1053 final Class<?>[] paramTypes = ArrayUtils.nullToEmpty(parameterTypes);
1054 final Method method = getMatchingAccessibleMethod(cls, methodName, paramTypes);
1055 requireNonNull(method, cls, methodName, paramTypes);
1056 return method.invoke(null, toVarArgs(method, ArrayUtils.nullToEmpty(args)));
1057 }
1058
1059 private static Method requireNonNull(final Method method, final Class<?> cls, final String methodName, final Class<?>[] parameterTypes)
1060 throws NoSuchMethodException {
1061 if (method == null) {
1062 throw new NoSuchMethodException(String.format("No method: %s.%s(%s)", ClassUtils.getName(cls), methodName,
1063 Streams.of(parameterTypes).map(ClassUtils::getName).collect(LangCollectors.joining(", "))));
1064 }
1065 return method;
1066 }
1067
1068 static Object[] toVarArgs(final Executable executable, final Object[] args)
1069 throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
1070 return executable.isVarArgs() ? toVarArgs(args, executable.getParameterTypes()) : args;
1071 }
1072
1073 /**
1074 * Gets an array of arguments in the canonical form, given an arguments array passed to a varargs method, for example an array with the declared number of
1075 * parameters, and whose last parameter is an array of the varargs type.
1076 * <p>
1077 * We follow the <a href="https://docs.oracle.com/javase/specs/jls/se21/html/jls-5.html#jls-5.1.2">JLS 5.1.2. Widening Primitive Conversion</a> rules.
1078 * </p>
1079 *
1080 * @param args The array of arguments passed to the varags method.
1081 * @param methodParameterTypes The declared array of method parameter types.
1082 * @return An array of the variadic arguments passed to the method.
1083 * @throws NoSuchMethodException Thrown if the constructor could not be found.
1084 * @throws IllegalAccessException Thrown if this {@code Constructor} object is enforcing Java language access control and the underlying constructor is
1085 * inaccessible.
1086 * @throws IllegalArgumentException Thrown if the number of actual and formal parameters differ; if an unwrapping conversion for primitive arguments
1087 * fails; or if, after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter
1088 * type by a method invocation conversion; if this constructor pertains to an enum type.
1089 * @throws InstantiationException Thrown if a class that declares the underlying constructor represents an abstract class.
1090 * @throws InvocationTargetException Thrown if an underlying constructor throws an exception.
1091 * @throws ExceptionInInitializerError Thrown if an initialization provoked by this method fails.
1092 * @see <a href="https://docs.oracle.com/javase/specs/jls/se21/html/jls-5.html#jls-5.1.2">JLS 5.1.2. Widening Primitive Conversion</a>
1093 */
1094 private static Object[] toVarArgs(final Object[] args, final Class<?>[] methodParameterTypes)
1095 throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
1096 final int mptLength = methodParameterTypes.length;
1097 if (args.length == mptLength) {
1098 final Object lastArg = args[args.length - 1];
1099 if (lastArg == null || lastArg.getClass().equals(methodParameterTypes[mptLength - 1])) {
1100 // The args array is already in the canonical form for the method.
1101 return args;
1102 }
1103 }
1104 // Construct a new array matching the method's declared parameter types.
1105 // Copy the normal (non-varargs) parameters
1106 final Object[] newArgs = ArrayUtils.arraycopy(args, 0, 0, mptLength - 1, () -> new Object[mptLength]);
1107 // Construct a new array for the variadic parameters
1108 final Class<?> varArgComponentType = methodParameterTypes[mptLength - 1].getComponentType();
1109 final Class<?> varArgComponentWrappedType = ClassUtils.primitiveToWrapper(varArgComponentType);
1110 final int varArgLength = args.length - mptLength + 1;
1111 // Copy the variadic arguments into the varargs array, converting types if needed.
1112 Object varArgsArray = Array.newInstance(varArgComponentWrappedType, varArgLength);
1113 final boolean primitiveOrWrapper = ClassUtils.isPrimitiveOrWrapper(varArgComponentWrappedType);
1114 for (int i = 0; i < varArgLength; i++) {
1115 final Object arg = args[mptLength - 1 + i];
1116 try {
1117 Array.set(varArgsArray, i, primitiveOrWrapper
1118 ? varArgComponentWrappedType.getConstructor(ClassUtils.wrapperToPrimitive(varArgComponentWrappedType)).newInstance(arg)
1119 : varArgComponentWrappedType.cast(arg));
1120 } catch (final InstantiationException e) {
1121 throw new IllegalArgumentException("Cannot convert vararg #" + i, e);
1122 }
1123 }
1124 if (varArgComponentType.isPrimitive()) {
1125 // unbox from wrapper type to primitive type
1126 varArgsArray = ArrayUtils.toPrimitive(varArgsArray);
1127 }
1128 // Store the varargs array in the last position of the array to return
1129 newArgs[mptLength - 1] = varArgsArray;
1130 // Return the canonical varargs array.
1131 return newArgs;
1132 }
1133
1134 /**
1135 * {@link MethodUtils} instances should NOT be constructed in standard programming. Instead, the class should be used as
1136 * {@code MethodUtils.getAccessibleMethod(method)}.
1137 *
1138 * <p>
1139 * This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
1140 * </p>
1141 *
1142 * @deprecated TODO Make private in 4.0.
1143 */
1144 @Deprecated
1145 public MethodUtils() {
1146 // empty
1147 }
1148 }