Coverage Report - org.apache.commons.beanutils.MethodUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
MethodUtils
69%
208/299
62%
120/192
5.243
MethodUtils$MethodDescriptor
77%
14/18
75%
12/16
5.243
 
 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  
 
 18  
 package org.apache.commons.beanutils;
 19  
 
 20  
 
 21  
 import java.lang.ref.Reference;
 22  
 import java.lang.ref.WeakReference;
 23  
 import java.lang.reflect.InvocationTargetException;
 24  
 import java.lang.reflect.Method;
 25  
 import java.lang.reflect.Modifier;
 26  
 import java.util.Collections;
 27  
 import java.util.Map;
 28  
 import java.util.WeakHashMap;
 29  
 
 30  
 import org.apache.commons.logging.Log;
 31  
 import org.apache.commons.logging.LogFactory;
 32  
 
 33  
 
 34  
 /**
 35  
  * <p> Utility reflection methods focussed on methods in general rather than properties in particular. </p>
 36  
  *
 37  
  * <h3>Known Limitations</h3>
 38  
  * <h4>Accessing Public Methods In A Default Access Superclass</h4>
 39  
  * <p>There is an issue when invoking public methods contained in a default access superclass.
 40  
  * Reflection locates these methods fine and correctly assigns them as public.
 41  
  * However, an <code>IllegalAccessException</code> is thrown if the method is invoked.</p>
 42  
  *
 43  
  * <p><code>MethodUtils</code> contains a workaround for this situation. 
 44  
  * It will attempt to call <code>setAccessible</code> on this method.
 45  
  * If this call succeeds, then the method can be invoked as normal.
 46  
  * This call will only succeed when the application has sufficient security privilages. 
 47  
  * If this call fails then a warning will be logged and the method may fail.</p>
 48  
  *
 49  
  * @author Craig R. McClanahan
 50  
  * @author Ralph Schaer
 51  
  * @author Chris Audley
 52  
  * @author Rey Fran&#231;ois
 53  
  * @author Gregor Ra&#253;man
 54  
  * @author Jan Sorensen
 55  
  * @author Robert Burrell Donkin
 56  
  */
 57  
 
 58  0
 public class MethodUtils {
 59  
 
 60  
     // --------------------------------------------------------- Private Methods
 61  
     
 62  
     /** 
 63  
      * Only log warning about accessibility work around once.
 64  
      * <p>
 65  
      * Note that this is broken when this class is deployed via a shared
 66  
      * classloader in a container, as the warning message will be emitted
 67  
      * only once, not once per webapp. However making the warning appear
 68  
      * once per webapp means having a map keyed by context classloader
 69  
      * which introduces nasty memory-leak problems. As this warning is
 70  
      * really optional we can ignore this problem; only one of the webapps
 71  
      * will get the warning in its logs but that should be good enough.
 72  
      */
 73  1
     private static boolean loggedAccessibleWarning = false;
 74  
     
 75  
     /** 
 76  
      * Indicates whether methods should be cached for improved performance.
 77  
      * <p>
 78  
      * Note that when this class is deployed via a shared classloader in
 79  
      * a container, this will affect all webapps. However making this
 80  
      * configurable per webapp would mean having a map keyed by context classloader
 81  
      * which may introduce memory-leak problems.
 82  
      */
 83  1
     private static boolean CACHE_METHODS = true;
 84  
 
 85  
     /** An empty class array */
 86  1
     private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0];
 87  
     /** An empty object array */
 88  1
     private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
 89  
 
 90  
     /**
 91  
      * Stores a cache of MethodDescriptor -> Method in a WeakHashMap.
 92  
      * <p>
 93  
      * The keys into this map only ever exist as temporary variables within
 94  
      * methods of this class, and are never exposed to users of this class.
 95  
      * This means that the WeakHashMap is used only as a mechanism for 
 96  
      * limiting the size of the cache, ie a way to tell the garbage collector
 97  
      * that the contents of the cache can be completely garbage-collected 
 98  
      * whenever it needs the memory. Whether this is a good approach to
 99  
      * this problem is doubtful; something like the commons-collections
 100  
      * LRUMap may be more appropriate (though of course selecting an
 101  
      * appropriate size is an issue).
 102  
      * <p>
 103  
      * This static variable is safe even when this code is deployed via a
 104  
      * shared classloader because it is keyed via a MethodDescriptor object
 105  
      * which has a Class as one of its members and that member is used in
 106  
      * the MethodDescriptor.equals method. So two components that load the same
 107  
      * class via different classloaders will generate non-equal MethodDescriptor
 108  
      * objects and hence end up with different entries in the map.
 109  
      */
 110  1
     private static final Map cache = Collections.synchronizedMap(new WeakHashMap());
 111  
     
 112  
     // --------------------------------------------------------- Public Methods
 113  
 
 114  
     /**
 115  
      * Set whether methods should be cached for greater performance or not,
 116  
      * default is <code>true</code>.
 117  
      *
 118  
      * @param cacheMethods <code>true</code> if methods should be
 119  
      * cached for greater performance, otherwise <code>false</code>
 120  
      * @since 1.8.0
 121  
      */
 122  
     public static synchronized void setCacheMethods(boolean cacheMethods) {
 123  3
         CACHE_METHODS = cacheMethods;
 124  3
         if (!CACHE_METHODS) {
 125  1
             clearCache();
 126  
         }
 127  3
     }
 128  
 
 129  
     /**
 130  
      * Clear the method cache.
 131  
      * @return the number of cached methods cleared
 132  
      * @since 1.8.0
 133  
      */
 134  
     public static synchronized int clearCache() {
 135  24
         int size = cache.size();
 136  24
         cache.clear();
 137  24
         return size;
 138  
     }
 139  
     
 140  
     /**
 141  
      * <p>Invoke a named method whose parameter type matches the object type.</p>
 142  
      *
 143  
      * <p>The behaviour of this method is less deterministic 
 144  
      * than <code>invokeExactMethod()</code>.
 145  
      * It loops through all methods with names that match
 146  
      * and then executes the first it finds with compatable parameters.</p>
 147  
      *
 148  
      * <p>This method supports calls to methods taking primitive parameters 
 149  
      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
 150  
      * would match a <code>boolean</code> primitive.</p>
 151  
      *
 152  
      * <p> This is a convenient wrapper for
 153  
      * {@link #invokeMethod(Object object,String methodName,Object [] args)}.
 154  
      * </p>
 155  
      *
 156  
      * @param object invoke method on this object
 157  
      * @param methodName get method with this name
 158  
      * @param arg use this argument
 159  
      * @return The value returned by the invoked method
 160  
      *
 161  
      * @throws NoSuchMethodException if there is no such accessible method
 162  
      * @throws InvocationTargetException wraps an exception thrown by the
 163  
      *  method invoked
 164  
      * @throws IllegalAccessException if the requested method is not accessible
 165  
      *  via reflection
 166  
      */
 167  
     public static Object invokeMethod(
 168  
             Object object,
 169  
             String methodName,
 170  
             Object arg)
 171  
             throws
 172  
             NoSuchMethodException,
 173  
             IllegalAccessException,
 174  
             InvocationTargetException {
 175  
 
 176  15
         Object[] args = {arg};
 177  15
         return invokeMethod(object, methodName, args);
 178  
 
 179  
     }
 180  
 
 181  
 
 182  
     /**
 183  
      * <p>Invoke a named method whose parameter type matches the object type.</p>
 184  
      *
 185  
      * <p>The behaviour of this method is less deterministic 
 186  
      * than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}. 
 187  
      * It loops through all methods with names that match
 188  
      * and then executes the first it finds with compatable parameters.</p>
 189  
      *
 190  
      * <p>This method supports calls to methods taking primitive parameters 
 191  
      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
 192  
      * would match a <code>boolean</code> primitive.</p>
 193  
      *
 194  
      * <p> This is a convenient wrapper for
 195  
      * {@link #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
 196  
      * </p>
 197  
      *
 198  
      * @param object invoke method on this object
 199  
      * @param methodName get method with this name
 200  
      * @param args use these arguments - treat null as empty array
 201  
      * @return The value returned by the invoked method
 202  
      *
 203  
      * @throws NoSuchMethodException if there is no such accessible method
 204  
      * @throws InvocationTargetException wraps an exception thrown by the
 205  
      *  method invoked
 206  
      * @throws IllegalAccessException if the requested method is not accessible
 207  
      *  via reflection
 208  
      */
 209  
     public static Object invokeMethod(
 210  
             Object object,
 211  
             String methodName,
 212  
             Object[] args)
 213  
             throws
 214  
             NoSuchMethodException,
 215  
             IllegalAccessException,
 216  
             InvocationTargetException {
 217  
         
 218  18
         if (args == null) {
 219  1
             args = EMPTY_OBJECT_ARRAY;
 220  
         }  
 221  18
         int arguments = args.length;
 222  18
         Class[] parameterTypes = new Class[arguments];
 223  37
         for (int i = 0; i < arguments; i++) {
 224  19
             parameterTypes[i] = args[i].getClass();
 225  
         }
 226  18
         return invokeMethod(object, methodName, args, parameterTypes);
 227  
 
 228  
     }
 229  
 
 230  
 
 231  
     /**
 232  
      * <p>Invoke a named method whose parameter type matches the object type.</p>
 233  
      *
 234  
      * <p>The behaviour of this method is less deterministic 
 235  
      * than {@link 
 236  
      * #invokeExactMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}. 
 237  
      * It loops through all methods with names that match
 238  
      * and then executes the first it finds with compatable parameters.</p>
 239  
      *
 240  
      * <p>This method supports calls to methods taking primitive parameters 
 241  
      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
 242  
      * would match a <code>boolean</code> primitive.</p>
 243  
      *
 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
 253  
      *  method invoked
 254  
      * @throws IllegalAccessException if the requested method is not accessible
 255  
      *  via reflection
 256  
      */
 257  
     public static Object invokeMethod(
 258  
             Object object,
 259  
             String methodName,
 260  
             Object[] args,
 261  
             Class[] parameterTypes)
 262  
                 throws
 263  
                     NoSuchMethodException,
 264  
                     IllegalAccessException,
 265  
                     InvocationTargetException {
 266  
                     
 267  24
         if (parameterTypes == null) {
 268  1
             parameterTypes = EMPTY_CLASS_PARAMETERS;
 269  
         }        
 270  24
         if (args == null) {
 271  1
             args = EMPTY_OBJECT_ARRAY;
 272  
         }  
 273  
 
 274  24
         Method method = getMatchingAccessibleMethod(
 275  
                 object.getClass(),
 276  
                 methodName,
 277  
                 parameterTypes);
 278  24
         if (method == null) {
 279  1
             throw new NoSuchMethodException("No such accessible method: " +
 280  
                     methodName + "() on object: " + object.getClass().getName());
 281  
         }
 282  23
         return method.invoke(object, args);
 283  
     }
 284  
 
 285  
 
 286  
     /**
 287  
      * <p>Invoke a method whose parameter type matches exactly the object
 288  
      * type.</p>
 289  
      *
 290  
      * <p> This is a convenient wrapper for
 291  
      * {@link #invokeExactMethod(Object object,String methodName,Object [] args)}.
 292  
      * </p>
 293  
      *
 294  
      * @param object invoke method on this object
 295  
      * @param methodName get method with this name
 296  
      * @param arg use this argument
 297  
      * @return The value returned by the invoked method
 298  
      *
 299  
      * @throws NoSuchMethodException if there is no such accessible method
 300  
      * @throws InvocationTargetException wraps an exception thrown by the
 301  
      *  method invoked
 302  
      * @throws IllegalAccessException if the requested method is not accessible
 303  
      *  via reflection
 304  
      */
 305  
     public static Object invokeExactMethod(
 306  
             Object object,
 307  
             String methodName,
 308  
             Object arg)
 309  
             throws
 310  
             NoSuchMethodException,
 311  
             IllegalAccessException,
 312  
             InvocationTargetException {
 313  
 
 314  3
         Object[] args = {arg};
 315  3
         return invokeExactMethod(object, methodName, args);
 316  
 
 317  
     }
 318  
 
 319  
 
 320  
     /**
 321  
      * <p>Invoke a method whose parameter types match exactly the object
 322  
      * types.</p>
 323  
      *
 324  
      * <p> This uses reflection to invoke the method obtained from a call to
 325  
      * <code>getAccessibleMethod()</code>.</p>
 326  
      *
 327  
      * @param object invoke method on this object
 328  
      * @param methodName get method with this name
 329  
      * @param args use these arguments - treat null as empty array
 330  
      * @return The value returned by the invoked method
 331  
      *
 332  
      * @throws NoSuchMethodException if there is no such accessible method
 333  
      * @throws InvocationTargetException wraps an exception thrown by the
 334  
      *  method invoked
 335  
      * @throws IllegalAccessException if the requested method is not accessible
 336  
      *  via reflection
 337  
      */
 338  
     public static Object invokeExactMethod(
 339  
             Object object,
 340  
             String methodName,
 341  
             Object[] args)
 342  
             throws
 343  
             NoSuchMethodException,
 344  
             IllegalAccessException,
 345  
             InvocationTargetException {
 346  5
         if (args == null) {
 347  1
             args = EMPTY_OBJECT_ARRAY;
 348  
         }  
 349  5
         int arguments = args.length;
 350  5
         Class[] parameterTypes = new Class[arguments];
 351  8
         for (int i = 0; i < arguments; i++) {
 352  3
             parameterTypes[i] = args[i].getClass();
 353  
         }
 354  5
         return invokeExactMethod(object, methodName, args, parameterTypes);
 355  
 
 356  
     }
 357  
 
 358  
 
 359  
     /**
 360  
      * <p>Invoke a method whose parameter types match exactly the parameter
 361  
      * types given.</p>
 362  
      *
 363  
      * <p>This uses reflection to invoke the method obtained from a call to
 364  
      * <code>getAccessibleMethod()</code>.</p>
 365  
      *
 366  
      * @param object invoke method on this object
 367  
      * @param methodName get method with this name
 368  
      * @param args use these arguments - treat null as empty array
 369  
      * @param parameterTypes match these parameters - treat null as empty array
 370  
      * @return The value returned by the invoked method
 371  
      *
 372  
      * @throws NoSuchMethodException if there is no such accessible method
 373  
      * @throws InvocationTargetException wraps an exception thrown by the
 374  
      *  method invoked
 375  
      * @throws IllegalAccessException if the requested method is not accessible
 376  
      *  via reflection
 377  
      */
 378  
     public static Object invokeExactMethod(
 379  
             Object object,
 380  
             String methodName,
 381  
             Object[] args,
 382  
             Class[] parameterTypes)
 383  
             throws
 384  
             NoSuchMethodException,
 385  
             IllegalAccessException,
 386  
             InvocationTargetException {
 387  
         
 388  11
         if (args == null) {
 389  1
             args = EMPTY_OBJECT_ARRAY;
 390  
         }  
 391  
                 
 392  11
         if (parameterTypes == null) {
 393  1
             parameterTypes = EMPTY_CLASS_PARAMETERS;
 394  
         }
 395  
 
 396  11
         Method method = getAccessibleMethod(
 397  
                 object.getClass(),
 398  
                 methodName,
 399  
                 parameterTypes);
 400  11
         if (method == null) {
 401  0
             throw new NoSuchMethodException("No such accessible method: " +
 402  
                     methodName + "() on object: " + object.getClass().getName());
 403  
         }
 404  11
         return method.invoke(object, args);
 405  
 
 406  
     }
 407  
 
 408  
     /**
 409  
      * <p>Invoke a static method whose parameter types match exactly the parameter
 410  
      * types given.</p>
 411  
      *
 412  
      * <p>This uses reflection to invoke the method obtained from a call to
 413  
      * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
 414  
      *
 415  
      * @param objectClass invoke static method on this class
 416  
      * @param methodName get method with this name
 417  
      * @param args use these arguments - treat null as empty array
 418  
      * @param parameterTypes match these parameters - treat null as empty array
 419  
      * @return The value returned by the invoked method
 420  
      *
 421  
      * @throws NoSuchMethodException if there is no such accessible method
 422  
      * @throws InvocationTargetException wraps an exception thrown by the
 423  
      *  method invoked
 424  
      * @throws IllegalAccessException if the requested method is not accessible
 425  
      *  via reflection
 426  
      * @since 1.8.0
 427  
      */
 428  
     public static Object invokeExactStaticMethod(
 429  
             Class objectClass,
 430  
             String methodName,
 431  
             Object[] args,
 432  
             Class[] parameterTypes)
 433  
             throws
 434  
             NoSuchMethodException,
 435  
             IllegalAccessException,
 436  
             InvocationTargetException {
 437  
         
 438  1
         if (args == null) {
 439  0
             args = EMPTY_OBJECT_ARRAY;
 440  
         }  
 441  
                 
 442  1
         if (parameterTypes == null) {
 443  0
             parameterTypes = EMPTY_CLASS_PARAMETERS;
 444  
         }
 445  
 
 446  1
         Method method = getAccessibleMethod(
 447  
                 objectClass,
 448  
                 methodName,
 449  
                 parameterTypes);
 450  1
         if (method == null) {
 451  0
             throw new NoSuchMethodException("No such accessible method: " +
 452  
                     methodName + "() on class: " + objectClass.getName());
 453  
         }
 454  1
         return method.invoke(null, args);
 455  
 
 456  
     }
 457  
 
 458  
     /**
 459  
      * <p>Invoke a named static method whose parameter type matches the object type.</p>
 460  
      *
 461  
      * <p>The behaviour of this method is less deterministic 
 462  
      * than {@link #invokeExactMethod(Object, String, Object[], Class[])}. 
 463  
      * It loops through all methods with names that match
 464  
      * and then executes the first it finds with compatable parameters.</p>
 465  
      *
 466  
      * <p>This method supports calls to methods taking primitive parameters 
 467  
      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
 468  
      * would match a <code>boolean</code> primitive.</p>
 469  
      *
 470  
      * <p> This is a convenient wrapper for
 471  
      * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args)}.
 472  
      * </p>
 473  
      *
 474  
      * @param objectClass invoke static method on this class
 475  
      * @param methodName get method with this name
 476  
      * @param arg use this argument
 477  
      * @return The value returned by the invoked method
 478  
      *
 479  
      * @throws NoSuchMethodException if there is no such accessible method
 480  
      * @throws InvocationTargetException wraps an exception thrown by the
 481  
      *  method invoked
 482  
      * @throws IllegalAccessException if the requested method is not accessible
 483  
      *  via reflection
 484  
      * @since 1.8.0
 485  
      */
 486  
     public static Object invokeStaticMethod(
 487  
             Class objectClass,
 488  
             String methodName,
 489  
             Object arg)
 490  
             throws
 491  
             NoSuchMethodException,
 492  
             IllegalAccessException,
 493  
             InvocationTargetException {
 494  
 
 495  0
         Object[] args = {arg};
 496  0
         return invokeStaticMethod (objectClass, methodName, args);
 497  
 
 498  
     }
 499  
 
 500  
 
 501  
     /**
 502  
      * <p>Invoke a named static method whose parameter type matches the object type.</p>
 503  
      *
 504  
      * <p>The behaviour of this method is less deterministic 
 505  
      * than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}. 
 506  
      * It loops through all methods with names that match
 507  
      * and then executes the first it finds with compatable parameters.</p>
 508  
      *
 509  
      * <p>This method supports calls to methods taking primitive parameters 
 510  
      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
 511  
      * would match a <code>boolean</code> primitive.</p>
 512  
      *
 513  
      * <p> This is a convenient wrapper for
 514  
      * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args,Class[] parameterTypes)}.
 515  
      * </p>
 516  
      *
 517  
      * @param objectClass invoke static method on this class
 518  
      * @param methodName get method with this name
 519  
      * @param args use these arguments - treat null as empty array
 520  
      * @return The value returned by the invoked method
 521  
      *
 522  
      * @throws NoSuchMethodException if there is no such accessible method
 523  
      * @throws InvocationTargetException wraps an exception thrown by the
 524  
      *  method invoked
 525  
      * @throws IllegalAccessException if the requested method is not accessible
 526  
      *  via reflection
 527  
      * @since 1.8.0
 528  
      */
 529  
     public static Object invokeStaticMethod(
 530  
             Class objectClass,
 531  
             String methodName,
 532  
             Object[] args)
 533  
             throws
 534  
             NoSuchMethodException,
 535  
             IllegalAccessException,
 536  
             InvocationTargetException {
 537  
         
 538  6
         if (args == null) {
 539  0
             args = EMPTY_OBJECT_ARRAY;
 540  
         }  
 541  6
         int arguments = args.length;
 542  6
         Class[] parameterTypes = new Class[arguments];
 543  7
         for (int i = 0; i < arguments; i++) {
 544  1
             parameterTypes[i] = args[i].getClass();
 545  
         }
 546  6
         return invokeStaticMethod (objectClass, methodName, args, parameterTypes);
 547  
 
 548  
     }
 549  
 
 550  
 
 551  
     /**
 552  
      * <p>Invoke a named static method whose parameter type matches the object type.</p>
 553  
      *
 554  
      * <p>The behaviour of this method is less deterministic 
 555  
      * than {@link 
 556  
      * #invokeExactStaticMethod(Class objectClass,String methodName,Object [] args,Class[] parameterTypes)}. 
 557  
      * It loops through all methods with names that match
 558  
      * and then executes the first it finds with compatable parameters.</p>
 559  
      *
 560  
      * <p>This method supports calls to methods taking primitive parameters 
 561  
      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
 562  
      * would match a <code>boolean</code> primitive.</p>
 563  
      *
 564  
      *
 565  
      * @param objectClass invoke static method on this class
 566  
      * @param methodName get method with this name
 567  
      * @param args use these arguments - treat null as empty array
 568  
      * @param parameterTypes match these parameters - treat null as empty array
 569  
      * @return The value returned by the invoked method
 570  
      *
 571  
      * @throws NoSuchMethodException if there is no such accessible method
 572  
      * @throws InvocationTargetException wraps an exception thrown by the
 573  
      *  method invoked
 574  
      * @throws IllegalAccessException if the requested method is not accessible
 575  
      *  via reflection
 576  
      * @since 1.8.0
 577  
      */
 578  
     public static Object invokeStaticMethod(
 579  
             Class objectClass,
 580  
             String methodName,
 581  
             Object[] args,
 582  
             Class[] parameterTypes)
 583  
                 throws
 584  
                     NoSuchMethodException,
 585  
                     IllegalAccessException,
 586  
                     InvocationTargetException {
 587  
                     
 588  6
         if (parameterTypes == null) {
 589  0
             parameterTypes = EMPTY_CLASS_PARAMETERS;
 590  
         }        
 591  6
         if (args == null) {
 592  0
             args = EMPTY_OBJECT_ARRAY;
 593  
         }  
 594  
 
 595  6
         Method method = getMatchingAccessibleMethod(
 596  
                 objectClass,
 597  
                 methodName,
 598  
                 parameterTypes);
 599  6
         if (method == null) {
 600  0
             throw new NoSuchMethodException("No such accessible method: " +
 601  
                     methodName + "() on class: " + objectClass.getName());
 602  
         }
 603  6
         return method.invoke(null, args);
 604  
     }
 605  
 
 606  
 
 607  
     /**
 608  
      * <p>Invoke a static method whose parameter type matches exactly the object
 609  
      * type.</p>
 610  
      *
 611  
      * <p> This is a convenient wrapper for
 612  
      * {@link #invokeExactStaticMethod(Class objectClass,String methodName,Object [] args)}.
 613  
      * </p>
 614  
      *
 615  
      * @param objectClass invoke static method on this class
 616  
      * @param methodName get method with this name
 617  
      * @param arg use this argument
 618  
      * @return The value returned by the invoked method
 619  
      *
 620  
      * @throws NoSuchMethodException if there is no such accessible method
 621  
      * @throws InvocationTargetException wraps an exception thrown by the
 622  
      *  method invoked
 623  
      * @throws IllegalAccessException if the requested method is not accessible
 624  
      *  via reflection
 625  
      * @since 1.8.0
 626  
      */
 627  
     public static Object invokeExactStaticMethod(
 628  
             Class objectClass,
 629  
             String methodName,
 630  
             Object arg)
 631  
             throws
 632  
             NoSuchMethodException,
 633  
             IllegalAccessException,
 634  
             InvocationTargetException {
 635  
 
 636  0
         Object[] args = {arg};
 637  0
         return invokeExactStaticMethod (objectClass, methodName, args);
 638  
 
 639  
     }
 640  
 
 641  
 
 642  
     /**
 643  
      * <p>Invoke a static method whose parameter types match exactly the object
 644  
      * types.</p>
 645  
      *
 646  
      * <p> This uses reflection to invoke the method obtained from a call to
 647  
      * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
 648  
      *
 649  
      * @param objectClass invoke static method on this class
 650  
      * @param methodName get method with this name
 651  
      * @param args use these arguments - treat null as empty array
 652  
      * @return The value returned by the invoked method
 653  
      *
 654  
      * @throws NoSuchMethodException if there is no such accessible method
 655  
      * @throws InvocationTargetException wraps an exception thrown by the
 656  
      *  method invoked
 657  
      * @throws IllegalAccessException if the requested method is not accessible
 658  
      *  via reflection
 659  
      * @since 1.8.0
 660  
      */
 661  
     public static Object invokeExactStaticMethod(
 662  
             Class objectClass,
 663  
             String methodName,
 664  
             Object[] args)
 665  
             throws
 666  
             NoSuchMethodException,
 667  
             IllegalAccessException,
 668  
             InvocationTargetException {
 669  0
         if (args == null) {
 670  0
             args = EMPTY_OBJECT_ARRAY;
 671  
         }  
 672  0
         int arguments = args.length;
 673  0
         Class[] parameterTypes = new Class[arguments];
 674  0
         for (int i = 0; i < arguments; i++) {
 675  0
             parameterTypes[i] = args[i].getClass();
 676  
         }
 677  0
         return invokeExactStaticMethod(objectClass, methodName, args, parameterTypes);
 678  
 
 679  
     }
 680  
 
 681  
 
 682  
     /**
 683  
      * <p>Return an accessible method (that is, one that can be invoked via
 684  
      * reflection) with given name and a single parameter.  If no such method
 685  
      * can be found, return <code>null</code>.
 686  
      * Basically, a convenience wrapper that constructs a <code>Class</code>
 687  
      * array for you.</p>
 688  
      *
 689  
      * @param clazz get method from this class
 690  
      * @param methodName get method with this name
 691  
      * @param parameterType taking this type of parameter
 692  
      * @return The accessible method
 693  
      */
 694  
     public static Method getAccessibleMethod(
 695  
             Class clazz,
 696  
             String methodName,
 697  
             Class parameterType) {
 698  
 
 699  5
         Class[] parameterTypes = {parameterType};
 700  5
         return getAccessibleMethod(clazz, methodName, parameterTypes);
 701  
 
 702  
     }
 703  
 
 704  
 
 705  
     /**
 706  
      * <p>Return an accessible method (that is, one that can be invoked via
 707  
      * reflection) with given name and parameters.  If no such method
 708  
      * can be found, return <code>null</code>.
 709  
      * This is just a convenient wrapper for
 710  
      * {@link #getAccessibleMethod(Method method)}.</p>
 711  
      *
 712  
      * @param clazz get method from this class
 713  
      * @param methodName get method with this name
 714  
      * @param parameterTypes with these parameters types
 715  
      * @return The accessible method
 716  
      */
 717  
     public static Method getAccessibleMethod(
 718  
             Class clazz,
 719  
             String methodName,
 720  
             Class[] parameterTypes) {
 721  
 
 722  
         try {
 723  21
             MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, true);
 724  
             // Check the cache first
 725  21
             Method method = getCachedMethod(md);
 726  21
             if (method != null) {
 727  9
                 return method;
 728  
             }
 729  
             
 730  12
             method =  getAccessibleMethod
 731  
                     (clazz, clazz.getMethod(methodName, parameterTypes));
 732  12
             cacheMethod(md, method);
 733  12
             return method;
 734  0
         } catch (NoSuchMethodException e) {
 735  0
             return (null);
 736  
         }
 737  
 
 738  
     }
 739  
 
 740  
 
 741  
     /**
 742  
      * <p>Return an accessible method (that is, one that can be invoked via
 743  
      * reflection) that implements the specified Method.  If no such method
 744  
      * can be found, return <code>null</code>.</p>
 745  
      *
 746  
      * @param method The method that we wish to call
 747  
      * @return The accessible method
 748  
      */
 749  
     public static Method getAccessibleMethod(Method method) {
 750  
 
 751  
         // Make sure we have a method to check
 752  88
         if (method == null) {
 753  0
             return (null);
 754  
         }
 755  
 
 756  88
         return getAccessibleMethod(method.getDeclaringClass(), method);
 757  
 
 758  
     }
 759  
 
 760  
 
 761  
 
 762  
     /**
 763  
      * <p>Return an accessible method (that is, one that can be invoked via
 764  
      * reflection) that implements the specified Method.  If no such method
 765  
      * can be found, return <code>null</code>.</p>
 766  
      *
 767  
      * @param clazz The class of the object
 768  
      * @param method The method that we wish to call
 769  
      * @return The accessible method
 770  
      * @since 1.8.0
 771  
      */
 772  
     public static Method getAccessibleMethod(Class clazz, Method method) {
 773  
 
 774  
         // Make sure we have a method to check
 775  1730
         if (method == null) {
 776  168
             return (null);
 777  
         }
 778  
 
 779  
         // If the requested method is not public we cannot call it
 780  1562
         if (!Modifier.isPublic(method.getModifiers())) {
 781  0
             return (null);
 782  
         }
 783  
 
 784  1562
         boolean sameClass = true;
 785  1562
         if (clazz == null) {
 786  0
             clazz = method.getDeclaringClass();
 787  
         } else {
 788  1562
             sameClass = clazz.equals(method.getDeclaringClass());
 789  1562
             if (!method.getDeclaringClass().isAssignableFrom(clazz)) {
 790  0
                 throw new IllegalArgumentException(clazz.getName() +
 791  
                         " is not assignable from " + method.getDeclaringClass().getName());
 792  
             }
 793  
         }
 794  
 
 795  
         // If the class is public, we are done
 796  1562
         if (Modifier.isPublic(clazz.getModifiers())) {
 797  1524
             if (!sameClass && !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
 798  0
                 setMethodAccessible(method); // Default access superclass workaround
 799  
             }
 800  1524
             return (method);
 801  
         }
 802  
 
 803  38
         String methodName      = method.getName();
 804  38
         Class[] parameterTypes = method.getParameterTypes();
 805  
 
 806  
         // Check the implemented interfaces and subinterfaces
 807  38
         method =
 808  
                 getAccessibleMethodFromInterfaceNest(clazz,
 809  
                         methodName,
 810  
                         parameterTypes);
 811  
 
 812  
         // Check the superclass chain
 813  38
         if (method == null) {
 814  24
             method = getAccessibleMethodFromSuperclass(clazz,
 815  
                         methodName,
 816  
                         parameterTypes);
 817  
         }
 818  
 
 819  38
         return (method);
 820  
 
 821  
     }
 822  
 
 823  
 
 824  
     // -------------------------------------------------------- Private Methods
 825  
 
 826  
     /**
 827  
      * <p>Return an accessible method (that is, one that can be invoked via
 828  
      * reflection) by scanning through the superclasses. If no such method
 829  
      * can be found, return <code>null</code>.</p>
 830  
      *
 831  
      * @param clazz Class to be checked
 832  
      * @param methodName Method name of the method we wish to call
 833  
      * @param parameterTypes The parameter type signatures
 834  
      */
 835  
     private static Method getAccessibleMethodFromSuperclass
 836  
             (Class clazz, String methodName, Class[] parameterTypes) {
 837  
 
 838  24
         Class parentClazz = clazz.getSuperclass();
 839  26
         while (parentClazz != null) {
 840  26
             if (Modifier.isPublic(parentClazz.getModifiers())) {
 841  
                 try {
 842  24
                     return parentClazz.getMethod(methodName, parameterTypes);
 843  14
                 } catch (NoSuchMethodException e) {
 844  14
                     return null;
 845  
                 }
 846  
             }
 847  2
             parentClazz = parentClazz.getSuperclass();
 848  
         }
 849  0
         return null;
 850  
     }
 851  
 
 852  
     /**
 853  
      * <p>Return an accessible method (that is, one that can be invoked via
 854  
      * reflection) that implements the specified method, by scanning through
 855  
      * all implemented interfaces and subinterfaces.  If no such method
 856  
      * can be found, return <code>null</code>.</p>
 857  
      *
 858  
      * <p> There isn't any good reason why this method must be private.
 859  
      * It is because there doesn't seem any reason why other classes should
 860  
      * call this rather than the higher level methods.</p>
 861  
      *
 862  
      * @param clazz Parent class for the interfaces to be checked
 863  
      * @param methodName Method name of the method we wish to call
 864  
      * @param parameterTypes The parameter type signatures
 865  
      */
 866  
     private static Method getAccessibleMethodFromInterfaceNest
 867  
             (Class clazz, String methodName, Class[] parameterTypes) {
 868  
 
 869  47
         Method method = null;
 870  
 
 871  
         // Search up the superclass chain
 872  175
         for (; clazz != null; clazz = clazz.getSuperclass()) {
 873  
 
 874  
             // Check the implemented interfaces of the parent class
 875  81
             Class[] interfaces = clazz.getInterfaces();
 876  87
             for (int i = 0; i < interfaces.length; i++) {
 877  
 
 878  
                 // Is this interface public?
 879  23
                 if (!Modifier.isPublic(interfaces[i].getModifiers())) {
 880  0
                     continue;
 881  
                 }
 882  
 
 883  
                 // Does the method exist on this interface?
 884  
                 try {
 885  23
                     method = interfaces[i].getDeclaredMethod(methodName,
 886  
                             parameterTypes);
 887  9
                 } catch (NoSuchMethodException e) {
 888  
                     /* Swallow, if no method is found after the loop then this
 889  
                      * method returns null.
 890  
                      */
 891  14
                 }
 892  23
                 if (method != null) {
 893  14
                     return method;
 894  
                 }
 895  
 
 896  
                 // Recursively check our parent interfaces
 897  9
                 method =
 898  
                         getAccessibleMethodFromInterfaceNest(interfaces[i],
 899  
                                 methodName,
 900  
                                 parameterTypes);
 901  9
                 if (method != null) {
 902  3
                     return method;
 903  
                 }
 904  
 
 905  
             }
 906  
 
 907  
         }
 908  
 
 909  
         // We did not find anything
 910  30
         return (null);
 911  
 
 912  
     }
 913  
 
 914  
     /**
 915  
      * <p>Find an accessible method that matches the given name and has compatible parameters.
 916  
      * Compatible parameters mean that every method parameter is assignable from 
 917  
      * the given parameters.
 918  
      * In other words, it finds a method with the given name 
 919  
      * that will take the parameters given.<p>
 920  
      *
 921  
      * <p>This method is slightly undeterminstic since it loops 
 922  
      * through methods names and return the first matching method.</p>
 923  
      * 
 924  
      * <p>This method is used by 
 925  
      * {@link 
 926  
      * #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
 927  
      *
 928  
      * <p>This method can match primitive parameter by passing in wrapper classes.
 929  
      * For example, a <code>Boolean</code> will match a primitive <code>boolean</code>
 930  
      * parameter.
 931  
      *
 932  
      * @param clazz find method in this class
 933  
      * @param methodName find method with this name
 934  
      * @param parameterTypes find method with compatible parameters 
 935  
      * @return The accessible method
 936  
      */
 937  
     public static Method getMatchingAccessibleMethod(
 938  
                                                 Class clazz,
 939  
                                                 String methodName,
 940  
                                                 Class[] parameterTypes) {
 941  
         // trace logging
 942  915
         Log log = LogFactory.getLog(MethodUtils.class);
 943  915
         if (log.isTraceEnabled()) {
 944  0
             log.trace("Matching name=" + methodName + " on " + clazz);
 945  
         }
 946  915
         MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, false);
 947  
         
 948  
         // see if we can find the method directly
 949  
         // most of the time this works and it's much faster
 950  
         try {
 951  
             // Check the cache first
 952  915
             Method method = getCachedMethod(md);
 953  915
             if (method != null) {
 954  9
                 return method;
 955  
             }
 956  
 
 957  906
             method = clazz.getMethod(methodName, parameterTypes);
 958  63
             if (log.isTraceEnabled()) {
 959  0
                 log.trace("Found straight match: " + method);
 960  0
                 log.trace("isPublic:" + Modifier.isPublic(method.getModifiers()));
 961  
             }
 962  
             
 963  63
             setMethodAccessible(method); // Default access superclass workaround
 964  
 
 965  63
             cacheMethod(md, method);
 966  63
             return method;
 967  
             
 968  843
         } catch (NoSuchMethodException e) { /* SWALLOW */ }
 969  
         
 970  
         // search through all methods 
 971  843
         int paramSize = parameterTypes.length;
 972  843
         Method bestMatch = null;
 973  843
         Method[] methods = clazz.getMethods();
 974  843
         float bestMatchCost = Float.MAX_VALUE;
 975  843
         float myCost = Float.MAX_VALUE;
 976  53816
         for (int i = 0, size = methods.length; i < size ; i++) {
 977  52973
             if (methods[i].getName().equals(methodName)) {
 978  
                 // log some trace information
 979  609
                 if (log.isTraceEnabled()) {
 980  0
                     log.trace("Found matching name:");
 981  0
                     log.trace(methods[i]);
 982  
                 }                
 983  
                 
 984  
                 // compare parameters
 985  609
                 Class[] methodsParams = methods[i].getParameterTypes();
 986  609
                 int methodParamSize = methodsParams.length;
 987  609
                 if (methodParamSize == paramSize) {          
 988  19
                     boolean match = true;
 989  36
                     for (int n = 0 ; n < methodParamSize; n++) {
 990  24
                         if (log.isTraceEnabled()) {
 991  0
                             log.trace("Param=" + parameterTypes[n].getName());
 992  0
                             log.trace("Method=" + methodsParams[n].getName());
 993  
                         }
 994  24
                         if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {
 995  7
                             if (log.isTraceEnabled()) {
 996  0
                                 log.trace(methodsParams[n] + " is not assignable from " 
 997  
                                             + parameterTypes[n]);
 998  
                             }    
 999  7
                             match = false;
 1000  7
                             break;
 1001  
                         }
 1002  
                     }
 1003  
                     
 1004  19
                     if (match) {
 1005  
                         // get accessible version of method
 1006  12
                         Method method = getAccessibleMethod(clazz, methods[i]);
 1007  12
                         if (method != null) {
 1008  12
                             if (log.isTraceEnabled()) {
 1009  0
                                 log.trace(method + " accessible version of " 
 1010  
                                             + methods[i]);
 1011  
                             }
 1012  12
                             setMethodAccessible(method); // Default access superclass workaround
 1013  12
                             myCost = getTotalTransformationCost(parameterTypes,method.getParameterTypes());
 1014  12
                             if ( myCost < bestMatchCost ) {
 1015  11
                                bestMatch = method;
 1016  11
                                bestMatchCost = myCost;
 1017  
                             }
 1018  
                         }
 1019  
                         
 1020  12
                         log.trace("Couldn't find accessible method.");
 1021  
                     }
 1022  
                 }
 1023  
             }
 1024  
         }
 1025  843
         if ( bestMatch != null ){
 1026  11
                  cacheMethod(md, bestMatch);
 1027  
         } else {
 1028  
         // didn't find a match
 1029  832
                log.trace("No match found.");
 1030  
         }
 1031  
         
 1032  843
         return bestMatch;                                        
 1033  
     }
 1034  
 
 1035  
     /**
 1036  
      * Try to make the method accessible
 1037  
      * @param method The source arguments
 1038  
      */
 1039  
     private static void setMethodAccessible(Method method) {
 1040  
         try {
 1041  
             //
 1042  
             // XXX Default access superclass workaround
 1043  
             //
 1044  
             // When a public class has a default access superclass
 1045  
             // with public methods, these methods are accessible.
 1046  
             // Calling them from compiled code works fine.
 1047  
             //
 1048  
             // Unfortunately, using reflection to invoke these methods
 1049  
             // seems to (wrongly) to prevent access even when the method
 1050  
             // modifer is public.
 1051  
             //
 1052  
             // The following workaround solves the problem but will only
 1053  
             // work from sufficiently privilages code. 
 1054  
             //
 1055  
             // Better workarounds would be greatfully accepted.
 1056  
             //
 1057  75
             if (!method.isAccessible()) {
 1058  75
                 method.setAccessible(true);
 1059  
             }
 1060  
             
 1061  0
         } catch (SecurityException se) {
 1062  
             // log but continue just in case the method.invoke works anyway
 1063  0
             Log log = LogFactory.getLog(MethodUtils.class);
 1064  0
             if (!loggedAccessibleWarning) {
 1065  0
                 boolean vulnerableJVM = false;
 1066  
                 try {
 1067  0
                     String specVersion = System.getProperty("java.specification.version");
 1068  0
                     if (specVersion.charAt(0) == '1' && 
 1069  
                             (specVersion.charAt(2) == '0' ||
 1070  
                              specVersion.charAt(2) == '1' ||
 1071  
                              specVersion.charAt(2) == '2' ||
 1072  
                              specVersion.charAt(2) == '3')) {
 1073  
                              
 1074  0
                         vulnerableJVM = true;
 1075  
                     }
 1076  0
                 } catch (SecurityException e) {
 1077  
                     // don't know - so display warning
 1078  0
                     vulnerableJVM = true;
 1079  0
                 }
 1080  0
                 if (vulnerableJVM) {
 1081  0
                     log.warn(
 1082  
                         "Current Security Manager restricts use of workarounds for reflection bugs "
 1083  
                         + " in pre-1.4 JVMs.");
 1084  
                 }
 1085  0
                 loggedAccessibleWarning = true;
 1086  
             }
 1087  0
             log.debug("Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", se);
 1088  75
         }
 1089  75
     }
 1090  
 
 1091  
     /**
 1092  
      * Returns the sum of the object transformation cost for each class in the source
 1093  
      * argument list.
 1094  
      * @param srcArgs The source arguments
 1095  
      * @param destArgs The destination arguments
 1096  
      * @return The total transformation cost
 1097  
      */
 1098  
     private static float getTotalTransformationCost(Class[] srcArgs, Class[] destArgs) {
 1099  
 
 1100  12
         float totalCost = 0.0f;
 1101  26
         for (int i = 0; i < srcArgs.length; i++) {
 1102  
             Class srcClass, destClass;
 1103  14
             srcClass = srcArgs[i];
 1104  14
             destClass = destArgs[i];
 1105  14
             totalCost += getObjectTransformationCost(srcClass, destClass);
 1106  
         }
 1107  
 
 1108  12
         return totalCost;
 1109  
     }
 1110  
     
 1111  
     /**
 1112  
      * Gets the number of steps required needed to turn the source class into the 
 1113  
      * destination class. This represents the number of steps in the object hierarchy 
 1114  
      * graph.
 1115  
      * @param srcClass The source class
 1116  
      * @param destClass The destination class
 1117  
      * @return The cost of transforming an object
 1118  
      */
 1119  
     private static float getObjectTransformationCost(Class srcClass, Class destClass) {
 1120  14
         float cost = 0.0f;
 1121  24
         while (destClass != null && !destClass.equals(srcClass)) {
 1122  14
             if (destClass.isInterface() && isAssignmentCompatible(destClass,srcClass)) {
 1123  
                 // slight penalty for interface match. 
 1124  
                 // we still want an exact match to override an interface match, but  
 1125  
                 // an interface match should override anything where we have to get a 
 1126  
                 // superclass.
 1127  4
                 cost += 0.25f;
 1128  4
                 break;
 1129  
             }
 1130  10
             cost++;
 1131  10
             destClass = destClass.getSuperclass();
 1132  
         }
 1133  
 
 1134  
         /*
 1135  
          * If the destination class is null, we've travelled all the way up to 
 1136  
          * an Object match. We'll penalize this by adding 1.5 to the cost.
 1137  
          */
 1138  14
         if (destClass == null) {
 1139  8
             cost += 1.5f;
 1140  
         }
 1141  
 
 1142  14
         return cost;
 1143  
     }
 1144  
     
 1145  
     
 1146  
     /**
 1147  
      * <p>Determine whether a type can be used as a parameter in a method invocation.
 1148  
      * This method handles primitive conversions correctly.</p>
 1149  
      *
 1150  
      * <p>In order words, it will match a <code>Boolean</code> to a <code>boolean</code>,
 1151  
      * a <code>Long</code> to a <code>long</code>,
 1152  
      * a <code>Float</code> to a <code>float</code>,
 1153  
      * a <code>Integer</code> to a <code>int</code>,
 1154  
      * and a <code>Double</code> to a <code>double</code>.
 1155  
      * Now logic widening matches are allowed.
 1156  
      * For example, a <code>Long</code> will not match a <code>int</code>.
 1157  
      *
 1158  
      * @param parameterType the type of parameter accepted by the method
 1159  
      * @param parameterization the type of parameter being tested 
 1160  
      *
 1161  
      * @return true if the assignement is compatible.
 1162  
      */
 1163  
     public static final boolean isAssignmentCompatible(Class parameterType, Class parameterization) {
 1164  
         // try plain assignment
 1165  39
         if (parameterType.isAssignableFrom(parameterization)) {
 1166  16
             return true;
 1167  
         }
 1168  
         
 1169  23
         if (parameterType.isPrimitive()) {
 1170  
             // this method does *not* do widening - you must specify exactly
 1171  
             // is this the right behaviour?
 1172  11
             Class parameterWrapperClazz = getPrimitiveWrapper(parameterType);
 1173  11
             if (parameterWrapperClazz != null) {
 1174  11
                 return parameterWrapperClazz.equals(parameterization);
 1175  
             }
 1176  
         }
 1177  
         
 1178  12
         return false;
 1179  
     }
 1180  
     
 1181  
     /**
 1182  
      * Gets the wrapper object class for the given primitive type class.
 1183  
      * For example, passing <code>boolean.class</code> returns <code>Boolean.class</code>
 1184  
      * @param primitiveType the primitive type class for which a match is to be found
 1185  
      * @return the wrapper type associated with the given primitive 
 1186  
      * or null if no match is found
 1187  
      */
 1188  
     public static Class getPrimitiveWrapper(Class primitiveType) {
 1189  
         // does anyone know a better strategy than comparing names?
 1190  11
         if (boolean.class.equals(primitiveType)) {
 1191  3
             return Boolean.class;
 1192  8
         } else if (float.class.equals(primitiveType)) {
 1193  3
             return Float.class;
 1194  5
         } else if (long.class.equals(primitiveType)) {
 1195  1
             return Long.class;
 1196  4
         } else if (int.class.equals(primitiveType)) {
 1197  2
             return Integer.class;
 1198  2
         } else if (short.class.equals(primitiveType)) {
 1199  0
             return Short.class;
 1200  2
         } else if (byte.class.equals(primitiveType)) {
 1201  0
             return Byte.class;
 1202  2
         } else if (double.class.equals(primitiveType)) {
 1203  2
             return Double.class;
 1204  0
         } else if (char.class.equals(primitiveType)) {
 1205  0
             return Character.class;
 1206  
         } else {
 1207  
             
 1208  0
             return null;
 1209  
         }
 1210  
     }
 1211  
 
 1212  
     /**
 1213  
      * Gets the class for the primitive type corresponding to the primitive wrapper class given.
 1214  
      * For example, an instance of <code>Boolean.class</code> returns a <code>boolean.class</code>. 
 1215  
      * @param wrapperType the 
 1216  
      * @return the primitive type class corresponding to the given wrapper class,
 1217  
      * null if no match is found
 1218  
      */
 1219  
     public static Class getPrimitiveType(Class wrapperType) {
 1220  
         // does anyone know a better strategy than comparing names?
 1221  0
         if (Boolean.class.equals(wrapperType)) {
 1222  0
             return boolean.class;
 1223  0
         } else if (Float.class.equals(wrapperType)) {
 1224  0
             return float.class;
 1225  0
         } else if (Long.class.equals(wrapperType)) {
 1226  0
             return long.class;
 1227  0
         } else if (Integer.class.equals(wrapperType)) {
 1228  0
             return int.class;
 1229  0
         } else if (Short.class.equals(wrapperType)) {
 1230  0
             return short.class;
 1231  0
         } else if (Byte.class.equals(wrapperType)) {
 1232  0
             return byte.class;
 1233  0
         } else if (Double.class.equals(wrapperType)) {
 1234  0
             return double.class;
 1235  0
         } else if (Character.class.equals(wrapperType)) {
 1236  0
             return char.class;
 1237  
         } else {
 1238  0
             Log log = LogFactory.getLog(MethodUtils.class);
 1239  0
             if (log.isDebugEnabled()) {
 1240  0
                 log.debug("Not a known primitive wrapper class: " + wrapperType);
 1241  
             }
 1242  0
             return null;
 1243  
         }
 1244  
     }
 1245  
     
 1246  
     /**
 1247  
      * Find a non primitive representation for given primitive class.
 1248  
      *
 1249  
      * @param clazz the class to find a representation for, not null
 1250  
      * @return the original class if it not a primitive. Otherwise the wrapper class. Not null
 1251  
      */
 1252  
     public static Class toNonPrimitiveClass(Class clazz) {
 1253  0
         if (clazz.isPrimitive()) {
 1254  0
             Class primitiveClazz = MethodUtils.getPrimitiveWrapper(clazz);
 1255  
             // the above method returns 
 1256  0
             if (primitiveClazz != null) {
 1257  0
                 return primitiveClazz;
 1258  
             } else {
 1259  0
                 return clazz;
 1260  
             }
 1261  
         } else {
 1262  0
             return clazz;
 1263  
         }
 1264  
     }
 1265  
 
 1266  
     /**
 1267  
      * Returns <tt>true</tt> if this method is a synthetic
 1268  
      * method; returns <tt>false</tt> otherwise.
 1269  
      *
 1270  
      * <p>
 1271  
      * Note synthetic methods were introduced in Java 1.5; therefore,
 1272  
      * this method can only return true on newer versions of java.
 1273  
      * </p>
 1274  
      *
 1275  
      * @param m the method.  null argument will return false.
 1276  
      * @return true if and only if this method is a synthetic
 1277  
      *         method as defined by the Java Language Specification.
 1278  
      * @since 1.8.4
 1279  
      */
 1280  
     public static boolean isSynthetic(Method m) {
 1281  8735
         return isSpecial(m , "isSynthetic");
 1282  
     }
 1283  
 
 1284  
     /**
 1285  
      * Returns <tt>true</tt> if this method is a bridge
 1286  
      * method; returns <tt>false</tt> otherwise.
 1287  
      *
 1288  
      * <p>
 1289  
      * Note synthetic methods were introduced in Java 1.5; therefore,
 1290  
      * this method can only return true on newer versions of java.
 1291  
      * </p>
 1292  
      *
 1293  
      * @param m the method.  null argument will return false.
 1294  
      * @return true if and only if this method is a bridge
 1295  
      * method as defined by the Java Language Specification.
 1296  
      * @since 1.8.4
 1297  
      */
 1298  
     public static boolean isBridge(Method m) {
 1299  8732
         return isSpecial(m , "isBridge");
 1300  
     }
 1301  
 
 1302  
     /**
 1303  
      * Returns <tt>true</tt> if this method was declared to take
 1304  
      * a variable number of arguments; returns <tt>false</tt>
 1305  
      * otherwise.
 1306  
      *
 1307  
      * <p>
 1308  
      * Note synthetic methods were introduced in Java 1.5; therefore,
 1309  
      * this method can only return true on newer versions of java.
 1310  
      * </p>
 1311  
      *
 1312  
      * @param m the method.  null argument will return false.
 1313  
      * @return <tt>true</tt> if an only if this method was declared to
 1314  
      *         take a variable number of arguments.
 1315  
      * @since 1.8.4
 1316  
      */
 1317  
     public static boolean isVarArgs(Method m) {
 1318  0
         return isSpecial(m , "isVarArgs");
 1319  
     }
 1320  
 
 1321  
     /**
 1322  
      * Calls the methodName on the passed in method using reflection.
 1323  
      * The methodName is assumed to be no arg and return a boolean.
 1324  
      * <p>
 1325  
      *     This method returns false given null arguments.
 1326  
      * </p>
 1327  
      * @param m the method
 1328  
      * @param methodName the methodName to call on the method object
 1329  
      * @return the boolean result of the method call
 1330  
      */
 1331  
     private static boolean isSpecial(Method m, String methodName) {
 1332  17467
         if (m == null || methodName == null) {
 1333  3632
             return false;
 1334  
         }
 1335  
 
 1336  
         try {
 1337  13835
             final Method special = m.getClass().getMethod(methodName, new Class[] {});
 1338  13835
             Boolean b = (Boolean) special.invoke(m, new Object[] {});
 1339  13835
             return b.booleanValue();
 1340  0
         } catch (NoSuchMethodException e) {
 1341  0
             return false;
 1342  0
         } catch (InvocationTargetException e) {
 1343  0
             return false;
 1344  0
         } catch (IllegalAccessException e) {
 1345  0
             return false;
 1346  
         }
 1347  
     }
 1348  
     
 1349  
 
 1350  
     /**
 1351  
      * Return the method from the cache, if present.
 1352  
      *
 1353  
      * @param md The method descriptor
 1354  
      * @return The cached method
 1355  
      */
 1356  
     private static Method getCachedMethod(MethodDescriptor md) {
 1357  936
         if (CACHE_METHODS) {
 1358  935
             Reference methodRef = (Reference)cache.get(md);
 1359  935
             if (methodRef != null) {
 1360  18
                 return (Method)methodRef.get();
 1361  
             }
 1362  
         }
 1363  918
         return null;
 1364  
     }
 1365  
 
 1366  
     /**
 1367  
      * Add a method to the cache.
 1368  
      *
 1369  
      * @param md The method descriptor
 1370  
      * @param method The method to cache
 1371  
      */
 1372  
     private static void cacheMethod(MethodDescriptor md, Method method) {
 1373  86
         if (CACHE_METHODS) {
 1374  85
             if (method != null) {
 1375  85
                 cache.put(md, new WeakReference(method));
 1376  
             }
 1377  
         }
 1378  86
     }
 1379  
 
 1380  
     /**
 1381  
      * Represents the key to looking up a Method by reflection.
 1382  
      */
 1383  0
     private static class MethodDescriptor {
 1384  
         private Class cls;
 1385  
         private String methodName;
 1386  
         private Class[] paramTypes;
 1387  
         private boolean exact;
 1388  
         private int hashCode;
 1389  
 
 1390  
         /**
 1391  
          * The sole constructor.
 1392  
          *
 1393  
          * @param cls  the class to reflect, must not be null
 1394  
          * @param methodName  the method name to obtain
 1395  
          * @param paramTypes the array of classes representing the paramater types
 1396  
          * @param exact whether the match has to be exact.
 1397  
          */
 1398  936
         public MethodDescriptor(Class cls, String methodName, Class[] paramTypes, boolean exact) {
 1399  936
             if (cls == null) {
 1400  0
                 throw new IllegalArgumentException("Class cannot be null");
 1401  
             }
 1402  936
             if (methodName == null) {
 1403  0
                 throw new IllegalArgumentException("Method Name cannot be null");
 1404  
             }
 1405  936
             if (paramTypes == null) {
 1406  0
                 paramTypes = EMPTY_CLASS_PARAMETERS;
 1407  
             }
 1408  
 
 1409  936
             this.cls = cls;
 1410  936
             this.methodName = methodName;
 1411  936
             this.paramTypes = paramTypes;
 1412  936
             this.exact= exact;
 1413  
 
 1414  936
             this.hashCode = methodName.length();
 1415  936
         }
 1416  
         /**
 1417  
          * Checks for equality.
 1418  
          * @param obj object to be tested for equality
 1419  
          * @return true, if the object describes the same Method.
 1420  
          */
 1421  
         public boolean equals(Object obj) {
 1422  207
             if (!(obj instanceof MethodDescriptor)) {
 1423  0
                 return false;
 1424  
             }
 1425  207
             MethodDescriptor md = (MethodDescriptor)obj;
 1426  
 
 1427  207
             return (
 1428  
                 exact == md.exact &&
 1429  
                 methodName.equals(md.methodName) &&
 1430  
                 cls.equals(md.cls) &&
 1431  
                 java.util.Arrays.equals(paramTypes, md.paramTypes)
 1432  
             );
 1433  
         }
 1434  
         /**
 1435  
          * Returns the string length of method name. I.e. if the
 1436  
          * hashcodes are different, the objects are different. If the
 1437  
          * hashcodes are the same, need to use the equals method to
 1438  
          * determine equality.
 1439  
          * @return the string length of method name.
 1440  
          */
 1441  
         public int hashCode() {
 1442  1020
             return hashCode;
 1443  
         }
 1444  
     }
 1445  
 }