001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.beanutils;
019
020
021import java.lang.ref.Reference;
022import java.lang.ref.WeakReference;
023import java.lang.reflect.InvocationTargetException;
024import java.lang.reflect.Method;
025import java.lang.reflect.Modifier;
026import java.util.Collections;
027import java.util.Map;
028import java.util.WeakHashMap;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032
033
034/**
035 * <p> Utility reflection methods focussed on methods in general rather than properties in particular. </p>
036 *
037 * <h3>Known Limitations</h3>
038 * <h4>Accessing Public Methods In A Default Access Superclass</h4>
039 * <p>There is an issue when invoking public methods contained in a default access superclass.
040 * Reflection locates these methods fine and correctly assigns them as public.
041 * However, an <code>IllegalAccessException</code> is thrown if the method is invoked.</p>
042 *
043 * <p><code>MethodUtils</code> contains a workaround for this situation. 
044 * It will attempt to call <code>setAccessible</code> on this method.
045 * If this call succeeds, then the method can be invoked as normal.
046 * This call will only succeed when the application has sufficient security privilages. 
047 * If this call fails then a warning will be logged and the method may fail.</p>
048 *
049 * @author Craig R. McClanahan
050 * @author Ralph Schaer
051 * @author Chris Audley
052 * @author Rey Fran&#231;ois
053 * @author Gregor Ra&#253;man
054 * @author Jan Sorensen
055 * @author Robert Burrell Donkin
056 */
057
058public class MethodUtils {
059
060    // --------------------------------------------------------- Private Methods
061    
062    /** 
063     * Only log warning about accessibility work around once.
064     * <p>
065     * Note that this is broken when this class is deployed via a shared
066     * classloader in a container, as the warning message will be emitted
067     * only once, not once per webapp. However making the warning appear
068     * once per webapp means having a map keyed by context classloader
069     * which introduces nasty memory-leak problems. As this warning is
070     * really optional we can ignore this problem; only one of the webapps
071     * will get the warning in its logs but that should be good enough.
072     */
073    private static boolean loggedAccessibleWarning = false;
074    
075    /** 
076     * Indicates whether methods should be cached for improved performance.
077     * <p>
078     * Note that when this class is deployed via a shared classloader in
079     * a container, this will affect all webapps. However making this
080     * configurable per webapp would mean having a map keyed by context classloader
081     * which may introduce memory-leak problems.
082     */
083    private static boolean CACHE_METHODS = true;
084
085    /** An empty class array */
086    private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0];
087    /** An empty object array */
088    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
089
090    /**
091     * Stores a cache of MethodDescriptor -> Method in a WeakHashMap.
092     * <p>
093     * The keys into this map only ever exist as temporary variables within
094     * methods of this class, and are never exposed to users of this class.
095     * This means that the WeakHashMap is used only as a mechanism for 
096     * limiting the size of the cache, ie a way to tell the garbage collector
097     * that the contents of the cache can be completely garbage-collected 
098     * whenever it needs the memory. Whether this is a good approach to
099     * 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    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        CACHE_METHODS = cacheMethods;
124        if (!CACHE_METHODS) {
125            clearCache();
126        }
127    }
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        int size = cache.size();
136        cache.clear();
137        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        Object[] args = {arg};
177        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        if (args == null) {
219            args = EMPTY_OBJECT_ARRAY;
220        }  
221        int arguments = args.length;
222        Class[] parameterTypes = new Class[arguments];
223        for (int i = 0; i < arguments; i++) {
224            parameterTypes[i] = args[i].getClass();
225        }
226        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        if (parameterTypes == null) {
268            parameterTypes = EMPTY_CLASS_PARAMETERS;
269        }        
270        if (args == null) {
271            args = EMPTY_OBJECT_ARRAY;
272        }  
273
274        Method method = getMatchingAccessibleMethod(
275                object.getClass(),
276                methodName,
277                parameterTypes);
278        if (method == null) {
279            throw new NoSuchMethodException("No such accessible method: " +
280                    methodName + "() on object: " + object.getClass().getName());
281        }
282        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        Object[] args = {arg};
315        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        if (args == null) {
347            args = EMPTY_OBJECT_ARRAY;
348        }  
349        int arguments = args.length;
350        Class[] parameterTypes = new Class[arguments];
351        for (int i = 0; i < arguments; i++) {
352            parameterTypes[i] = args[i].getClass();
353        }
354        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        if (args == null) {
389            args = EMPTY_OBJECT_ARRAY;
390        }  
391                
392        if (parameterTypes == null) {
393            parameterTypes = EMPTY_CLASS_PARAMETERS;
394        }
395
396        Method method = getAccessibleMethod(
397                object.getClass(),
398                methodName,
399                parameterTypes);
400        if (method == null) {
401            throw new NoSuchMethodException("No such accessible method: " +
402                    methodName + "() on object: " + object.getClass().getName());
403        }
404        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        if (args == null) {
439            args = EMPTY_OBJECT_ARRAY;
440        }  
441                
442        if (parameterTypes == null) {
443            parameterTypes = EMPTY_CLASS_PARAMETERS;
444        }
445
446        Method method = getAccessibleMethod(
447                objectClass,
448                methodName,
449                parameterTypes);
450        if (method == null) {
451            throw new NoSuchMethodException("No such accessible method: " +
452                    methodName + "() on class: " + objectClass.getName());
453        }
454        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        Object[] args = {arg};
496        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        if (args == null) {
539            args = EMPTY_OBJECT_ARRAY;
540        }  
541        int arguments = args.length;
542        Class[] parameterTypes = new Class[arguments];
543        for (int i = 0; i < arguments; i++) {
544            parameterTypes[i] = args[i].getClass();
545        }
546        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        if (parameterTypes == null) {
589            parameterTypes = EMPTY_CLASS_PARAMETERS;
590        }        
591        if (args == null) {
592            args = EMPTY_OBJECT_ARRAY;
593        }  
594
595        Method method = getMatchingAccessibleMethod(
596                objectClass,
597                methodName,
598                parameterTypes);
599        if (method == null) {
600            throw new NoSuchMethodException("No such accessible method: " +
601                    methodName + "() on class: " + objectClass.getName());
602        }
603        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        Object[] args = {arg};
637        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        if (args == null) {
670            args = EMPTY_OBJECT_ARRAY;
671        }  
672        int arguments = args.length;
673        Class[] parameterTypes = new Class[arguments];
674        for (int i = 0; i < arguments; i++) {
675            parameterTypes[i] = args[i].getClass();
676        }
677        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        Class[] parameterTypes = {parameterType};
700        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            MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, true);
724            // Check the cache first
725            Method method = getCachedMethod(md);
726            if (method != null) {
727                return method;
728            }
729            
730            method =  getAccessibleMethod
731                    (clazz, clazz.getMethod(methodName, parameterTypes));
732            cacheMethod(md, method);
733            return method;
734        } catch (NoSuchMethodException e) {
735            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        if (method == null) {
753            return (null);
754        }
755
756        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        if (method == null) {
776            return (null);
777        }
778
779        // If the requested method is not public we cannot call it
780        if (!Modifier.isPublic(method.getModifiers())) {
781            return (null);
782        }
783
784        boolean sameClass = true;
785        if (clazz == null) {
786            clazz = method.getDeclaringClass();
787        } else {
788            sameClass = clazz.equals(method.getDeclaringClass());
789            if (!method.getDeclaringClass().isAssignableFrom(clazz)) {
790                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        if (Modifier.isPublic(clazz.getModifiers())) {
797            if (!sameClass && !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
798                setMethodAccessible(method); // Default access superclass workaround
799            }
800            return (method);
801        }
802
803        String methodName      = method.getName();
804        Class[] parameterTypes = method.getParameterTypes();
805
806        // Check the implemented interfaces and subinterfaces
807        method =
808                getAccessibleMethodFromInterfaceNest(clazz,
809                        methodName,
810                        parameterTypes);
811
812        // Check the superclass chain
813        if (method == null) {
814            method = getAccessibleMethodFromSuperclass(clazz,
815                        methodName,
816                        parameterTypes);
817        }
818
819        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        Class parentClazz = clazz.getSuperclass();
839        while (parentClazz != null) {
840            if (Modifier.isPublic(parentClazz.getModifiers())) {
841                try {
842                    return parentClazz.getMethod(methodName, parameterTypes);
843                } catch (NoSuchMethodException e) {
844                    return null;
845                }
846            }
847            parentClazz = parentClazz.getSuperclass();
848        }
849        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        Method method = null;
870
871        // Search up the superclass chain
872        for (; clazz != null; clazz = clazz.getSuperclass()) {
873
874            // Check the implemented interfaces of the parent class
875            Class[] interfaces = clazz.getInterfaces();
876            for (int i = 0; i < interfaces.length; i++) {
877
878                // Is this interface public?
879                if (!Modifier.isPublic(interfaces[i].getModifiers())) {
880                    continue;
881                }
882
883                // Does the method exist on this interface?
884                try {
885                    method = interfaces[i].getDeclaredMethod(methodName,
886                            parameterTypes);
887                } catch (NoSuchMethodException e) {
888                    /* Swallow, if no method is found after the loop then this
889                     * method returns null.
890                     */
891                }
892                if (method != null) {
893                    return method;
894                }
895
896                // Recursively check our parent interfaces
897                method =
898                        getAccessibleMethodFromInterfaceNest(interfaces[i],
899                                methodName,
900                                parameterTypes);
901                if (method != null) {
902                    return method;
903                }
904
905            }
906
907        }
908
909        // We did not find anything
910        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        Log log = LogFactory.getLog(MethodUtils.class);
943        if (log.isTraceEnabled()) {
944            log.trace("Matching name=" + methodName + " on " + clazz);
945        }
946        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            Method method = getCachedMethod(md);
953            if (method != null) {
954                return method;
955            }
956
957            method = clazz.getMethod(methodName, parameterTypes);
958            if (log.isTraceEnabled()) {
959                log.trace("Found straight match: " + method);
960                log.trace("isPublic:" + Modifier.isPublic(method.getModifiers()));
961            }
962            
963            setMethodAccessible(method); // Default access superclass workaround
964
965            cacheMethod(md, method);
966            return method;
967            
968        } catch (NoSuchMethodException e) { /* SWALLOW */ }
969        
970        // search through all methods 
971        int paramSize = parameterTypes.length;
972        Method bestMatch = null;
973        Method[] methods = clazz.getMethods();
974        float bestMatchCost = Float.MAX_VALUE;
975        float myCost = Float.MAX_VALUE;
976        for (int i = 0, size = methods.length; i < size ; i++) {
977            if (methods[i].getName().equals(methodName)) {
978                // log some trace information
979                if (log.isTraceEnabled()) {
980                    log.trace("Found matching name:");
981                    log.trace(methods[i]);
982                }                
983                
984                // compare parameters
985                Class[] methodsParams = methods[i].getParameterTypes();
986                int methodParamSize = methodsParams.length;
987                if (methodParamSize == paramSize) {          
988                    boolean match = true;
989                    for (int n = 0 ; n < methodParamSize; n++) {
990                        if (log.isTraceEnabled()) {
991                            log.trace("Param=" + parameterTypes[n].getName());
992                            log.trace("Method=" + methodsParams[n].getName());
993                        }
994                        if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {
995                            if (log.isTraceEnabled()) {
996                                log.trace(methodsParams[n] + " is not assignable from " 
997                                            + parameterTypes[n]);
998                            }    
999                            match = false;
1000                            break;
1001                        }
1002                    }
1003                    
1004                    if (match) {
1005                        // get accessible version of method
1006                        Method method = getAccessibleMethod(clazz, methods[i]);
1007                        if (method != null) {
1008                            if (log.isTraceEnabled()) {
1009                                log.trace(method + " accessible version of " 
1010                                            + methods[i]);
1011                            }
1012                            setMethodAccessible(method); // Default access superclass workaround
1013                            myCost = getTotalTransformationCost(parameterTypes,method.getParameterTypes());
1014                            if ( myCost < bestMatchCost ) {
1015                               bestMatch = method;
1016                               bestMatchCost = myCost;
1017                            }
1018                        }
1019                        
1020                        log.trace("Couldn't find accessible method.");
1021                    }
1022                }
1023            }
1024        }
1025        if ( bestMatch != null ){
1026                 cacheMethod(md, bestMatch);
1027        } else {
1028        // didn't find a match
1029               log.trace("No match found.");
1030        }
1031        
1032        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            if (!method.isAccessible()) {
1058                method.setAccessible(true);
1059            }
1060            
1061        } catch (SecurityException se) {
1062            // log but continue just in case the method.invoke works anyway
1063            Log log = LogFactory.getLog(MethodUtils.class);
1064            if (!loggedAccessibleWarning) {
1065                boolean vulnerableJVM = false;
1066                try {
1067                    String specVersion = System.getProperty("java.specification.version");
1068                    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                        vulnerableJVM = true;
1075                    }
1076                } catch (SecurityException e) {
1077                    // don't know - so display warning
1078                    vulnerableJVM = true;
1079                }
1080                if (vulnerableJVM) {
1081                    log.warn(
1082                        "Current Security Manager restricts use of workarounds for reflection bugs "
1083                        + " in pre-1.4 JVMs.");
1084                }
1085                loggedAccessibleWarning = true;
1086            }
1087            log.debug("Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", se);
1088        }
1089    }
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        float totalCost = 0.0f;
1101        for (int i = 0; i < srcArgs.length; i++) {
1102            Class srcClass, destClass;
1103            srcClass = srcArgs[i];
1104            destClass = destArgs[i];
1105            totalCost += getObjectTransformationCost(srcClass, destClass);
1106        }
1107
1108        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        float cost = 0.0f;
1121        while (destClass != null && !destClass.equals(srcClass)) {
1122            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                cost += 0.25f;
1128                break;
1129            }
1130            cost++;
1131            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        if (destClass == null) {
1139            cost += 1.5f;
1140        }
1141
1142        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        if (parameterType.isAssignableFrom(parameterization)) {
1166            return true;
1167        }
1168        
1169        if (parameterType.isPrimitive()) {
1170            // this method does *not* do widening - you must specify exactly
1171            // is this the right behaviour?
1172            Class parameterWrapperClazz = getPrimitiveWrapper(parameterType);
1173            if (parameterWrapperClazz != null) {
1174                return parameterWrapperClazz.equals(parameterization);
1175            }
1176        }
1177        
1178        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        if (boolean.class.equals(primitiveType)) {
1191            return Boolean.class;
1192        } else if (float.class.equals(primitiveType)) {
1193            return Float.class;
1194        } else if (long.class.equals(primitiveType)) {
1195            return Long.class;
1196        } else if (int.class.equals(primitiveType)) {
1197            return Integer.class;
1198        } else if (short.class.equals(primitiveType)) {
1199            return Short.class;
1200        } else if (byte.class.equals(primitiveType)) {
1201            return Byte.class;
1202        } else if (double.class.equals(primitiveType)) {
1203            return Double.class;
1204        } else if (char.class.equals(primitiveType)) {
1205            return Character.class;
1206        } else {
1207            
1208            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        if (Boolean.class.equals(wrapperType)) {
1222            return boolean.class;
1223        } else if (Float.class.equals(wrapperType)) {
1224            return float.class;
1225        } else if (Long.class.equals(wrapperType)) {
1226            return long.class;
1227        } else if (Integer.class.equals(wrapperType)) {
1228            return int.class;
1229        } else if (Short.class.equals(wrapperType)) {
1230            return short.class;
1231        } else if (Byte.class.equals(wrapperType)) {
1232            return byte.class;
1233        } else if (Double.class.equals(wrapperType)) {
1234            return double.class;
1235        } else if (Character.class.equals(wrapperType)) {
1236            return char.class;
1237        } else {
1238            Log log = LogFactory.getLog(MethodUtils.class);
1239            if (log.isDebugEnabled()) {
1240                log.debug("Not a known primitive wrapper class: " + wrapperType);
1241            }
1242            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        if (clazz.isPrimitive()) {
1254            Class primitiveClazz = MethodUtils.getPrimitiveWrapper(clazz);
1255            // the above method returns 
1256            if (primitiveClazz != null) {
1257                return primitiveClazz;
1258            } else {
1259                return clazz;
1260            }
1261        } else {
1262            return clazz;
1263        }
1264    }
1265    
1266
1267    /**
1268     * Return the method from the cache, if present.
1269     *
1270     * @param md The method descriptor
1271     * @return The cached method
1272     */
1273    private static Method getCachedMethod(MethodDescriptor md) {
1274        if (CACHE_METHODS) {
1275            Reference methodRef = (Reference)cache.get(md);
1276            if (methodRef != null) {
1277                return (Method)methodRef.get();
1278            }
1279        }
1280        return null;
1281    }
1282
1283    /**
1284     * Add a method to the cache.
1285     *
1286     * @param md The method descriptor
1287     * @param method The method to cache
1288     */
1289    private static void cacheMethod(MethodDescriptor md, Method method) {
1290        if (CACHE_METHODS) {
1291            if (method != null) {
1292                cache.put(md, new WeakReference(method));
1293            }
1294        }
1295    }
1296
1297    /**
1298     * Represents the key to looking up a Method by reflection.
1299     */
1300    private static class MethodDescriptor {
1301        private Class cls;
1302        private String methodName;
1303        private Class[] paramTypes;
1304        private boolean exact;
1305        private int hashCode;
1306
1307        /**
1308         * The sole constructor.
1309         *
1310         * @param cls  the class to reflect, must not be null
1311         * @param methodName  the method name to obtain
1312         * @param paramTypes the array of classes representing the paramater types
1313         * @param exact whether the match has to be exact.
1314         */
1315        public MethodDescriptor(Class cls, String methodName, Class[] paramTypes, boolean exact) {
1316            if (cls == null) {
1317                throw new IllegalArgumentException("Class cannot be null");
1318            }
1319            if (methodName == null) {
1320                throw new IllegalArgumentException("Method Name cannot be null");
1321            }
1322            if (paramTypes == null) {
1323                paramTypes = EMPTY_CLASS_PARAMETERS;
1324            }
1325
1326            this.cls = cls;
1327            this.methodName = methodName;
1328            this.paramTypes = paramTypes;
1329            this.exact= exact;
1330
1331            this.hashCode = methodName.length();
1332        }
1333        /**
1334         * Checks for equality.
1335         * @param obj object to be tested for equality
1336         * @return true, if the object describes the same Method.
1337         */
1338        public boolean equals(Object obj) {
1339            if (!(obj instanceof MethodDescriptor)) {
1340                return false;
1341            }
1342            MethodDescriptor md = (MethodDescriptor)obj;
1343
1344            return (
1345                exact == md.exact &&
1346                methodName.equals(md.methodName) &&
1347                cls.equals(md.cls) &&
1348                java.util.Arrays.equals(paramTypes, md.paramTypes)
1349            );
1350        }
1351        /**
1352         * Returns the string length of method name. I.e. if the
1353         * hashcodes are different, the objects are different. If the
1354         * hashcodes are the same, need to use the equals method to
1355         * determine equality.
1356         * @return the string length of method name.
1357         */
1358        public int hashCode() {
1359            return hashCode;
1360        }
1361    }
1362}