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