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