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