View Javadoc

1   /*
2    * @(#)MethodUtils.java Feb 1, 2003 Copyright (c) 2002-2005 Delft University of Technology Jaffalaan 5, 2628
3    * BX Delft, the Netherlands. All rights reserved. This software is proprietary information of Delft
4    * University of Technology The code is published under the Lesser General Public License
5    */
6   package nl.tudelft.simulation.language.reflection;
7   
8   import java.lang.reflect.Array;
9   import java.lang.reflect.Constructor;
10  import java.lang.reflect.Field;
11  import java.lang.reflect.Method;
12  import java.lang.reflect.Modifier;
13  import java.util.ArrayList;
14  import java.util.Arrays;
15  import java.util.Collections;
16  import java.util.HashMap;
17  import java.util.List;
18  import java.util.Map;
19  
20  import nl.tudelft.simulation.language.primitives.Primitive;
21  
22  /***
23   * ClassUtil is a utility class providing assistance for Java Classes.
24   * <p>
25   * (c) copyright 2002-2005 <a href="http://www.simulation.tudelft.nl">Delft University of Technology </a>, the
26   * Netherlands.
27   * <p>
28   * See for project information <a
29   * href="http://www.simulation.tudelft.nl/dsol/language">www.simulation.tudelft.nl/language </a> <br>
30   * License of use: <a href="http://www.gnu.org/copyleft/lesser.html">Lesser General Public License (LGPL)
31   * </a>, no warranty
32   * 
33   * @author <a href="http://www.peter-jacobs.com/index.htm">Peter Jacobs </a>, <a
34   *         href="mailto:nlang@fbk.eur.nl">Niels Lang </a><a
35   *         href="mailto:a.verbraeck@tbm.tudelft.nl">Alexander Verbraeck </a>
36   * @version $Revision: 1.8 $ $Date: 2005/07/04 12:21:22 $
37   * @since 1.5
38   */
39  public final class ClassUtil
40  {
41      /*** CACHE reflects the internal repository CACHE. */
42      private static final Map<String, Object> CACHE = Collections
43              .synchronizedMap(new HashMap<String, Object>());
44  
45      /***
46       * constructs a new ClassUtil.
47       */
48      private ClassUtil()
49      {
50          super();
51          // unreachable code
52      }
53  
54      /*** ************ CONSTRUCTOR UTILITIES *********** */
55      /***
56       * gets all the constructors of a class and adds the result to result.
57       * 
58       * @param clazz
59       *            the class
60       * @param result
61       *            the resulting set
62       * @return result
63       */
64      public static Constructor[] getAllConstructors(final Class<?> clazz, final Constructor[] result)
65      {
66          List<Constructor> list = new ArrayList<Constructor>(Arrays.asList(result));
67          list.addAll(Arrays.asList(clazz.getDeclaredConstructors()));
68          if (clazz.getSuperclass() != null)
69          {
70              return ClassUtil.getAllConstructors(clazz.getSuperclass(), list.toArray(new Constructor[list
71                      .size()]));
72          }
73          return list.toArray(new Constructor[list.size()]);
74      }
75  
76      /***
77       * returns the interface method.
78       * 
79       * @param clazz
80       *            the class to start with
81       * @param callerClass
82       *            the calling class
83       * @param parameterTypes
84       *            the parameterTypes
85       * @return Constructor
86       * @throws NoSuchMethodException
87       *             if the method cannot be resolved
88       */
89      public static Constructor resolveConstructor(final Class<?> clazz, final Class<?> callerClass,
90              final Class[] parameterTypes) throws NoSuchMethodException
91      {
92          Constructor<?> constructor = ClassUtil.resolveConstructor(clazz, parameterTypes);
93          if (ClassUtil.isVisible(constructor, callerClass.getClass()))
94          {
95              return constructor;
96          }
97          throw new NoSuchMethodException("constructor resolved but not visible");
98      }
99  
100     /***
101      * returns the interface method.
102      * 
103      * @param clazz
104      *            the class to start with
105      * @param parameterTypes
106      *            the parameterTypes
107      * @return Constructor
108      * @throws NoSuchMethodException
109      *             if the method cannot be resolved
110      */
111     public static Constructor<?> resolveConstructor(final Class<?> clazz, final Class[] parameterTypes)
112             throws NoSuchMethodException
113     {
114         try
115         {
116             return resolveConstructorSuper(clazz, (Class[]) ClassUtil.checkInput(parameterTypes, Class.class));
117         }
118         catch (Exception exception)
119         {
120             String className = clazz.getName();
121             if (className.indexOf("$") >= 0)
122             {
123                 Class<?> parentClass = null;
124                 try
125                 {
126                     parentClass = Class.forName(className.substring(0, className.lastIndexOf("$")));
127                 }
128                 catch (Exception e2)
129                 {
130                     throw new NoSuchMethodException("class " + parentClass
131                             + " not found to resolve constructor");
132                 }
133                 return ClassUtil.resolveConstructor(parentClass, (Class[]) ClassUtil.checkInput(
134                         parameterTypes, Class.class));
135             }
136             throw new NoSuchMethodException("class " + clazz + " does not contain constructor");
137         }
138     }
139 
140     /***
141      * returns the constructor.
142      * 
143      * @param clazz
144      *            the clazz to start with
145      * @param arguments
146      *            the arguments
147      * @return Constructor
148      * @throws NoSuchMethodException
149      *             on lookup failure
150      */
151     public static Constructor<?> resolveConstructor(final Class<?> clazz, final Object[] arguments)
152             throws NoSuchMethodException
153     {
154         Class[] parameterTypes = ClassUtil.getClass(arguments);
155         String key = "CONSTRUCTOR:" + clazz + "@" + FieldSignature.toDescriptor(parameterTypes);
156         if (CACHE.containsKey(key))
157         {
158             return (Constructor<?>) CACHE.get(key);
159         }
160         try
161         {
162             return ClassUtil.resolveConstructor(clazz, parameterTypes);
163         }
164         catch (NoSuchMethodException noSuchMethodException)
165         {
166             // We get all constructors
167             Constructor[] constructors = ClassUtil.getAllConstructors(clazz, new Constructor[0]);
168             // now we match the signatures
169             constructors = ClassUtil.matchSignature(constructors, parameterTypes);
170             // Now we find the most specific
171             Constructor result = ClassUtil.getSpecificConstructor(constructors);
172             CACHE.put(key, result);
173             return result;
174         }
175     }
176 
177     /*** ************ FIELD UTILITIES *********** */
178     /***
179      * resolves the field for a class, taking into account inner classes.
180      * 
181      * @param clazz
182      *            the class to resolve the field for, including inner classes
183      * @param fieldName
184      *            name of the field
185      * @return Field the field
186      * @throws NoSuchFieldException
187      *             on no such field
188      */
189     public static Field resolveField(final Class<?> clazz, final String fieldName)
190             throws NoSuchFieldException
191     {
192         try
193         {
194             return resolveFieldSuper(clazz, fieldName);
195         }
196         catch (NoSuchFieldException noSuchFieldException)
197         {
198             String className = clazz.getName();
199             if (className.indexOf("$") >= 0)
200             {
201                 Class<?> clazz2 = null;
202                 try
203                 {
204                     clazz2 = Class.forName(className.substring(0, className.lastIndexOf("$")));
205                 }
206                 catch (ClassNotFoundException classNotFoundException)
207                 {
208                     throw new NoSuchFieldException("class " + clazz + " not found to resolve field "
209                             + fieldName);
210                 }
211                 return ClassUtil.resolveField(clazz2, fieldName);
212             }
213             throw new NoSuchFieldException("class " + clazz + " does not contain field " + fieldName);
214         }
215     }
216 
217     /***
218      * returns the field.
219      * 
220      * @param clazz
221      *            the class to start with
222      * @param callerClass
223      *            the calling class
224      * @param name
225      *            the fieldName
226      * @return Constructor
227      * @throws NoSuchFieldException
228      *             if the method cannot be resolved
229      */
230     public static Field resolveField(final Class clazz, final Class callerClass, final String name)
231             throws NoSuchFieldException
232     {
233         Field field = ClassUtil.resolveField(clazz, name);
234         if (ClassUtil.isVisible(field, callerClass.getClass()))
235         {
236             return field;
237         }
238         throw new NoSuchFieldException("field resolved but not visible");
239     }
240 
241     /***
242      * resolves the field for a given object instance.
243      * 
244      * @param object
245      *            the object to resolve the field for
246      * @param fieldName
247      *            name of the field to resolve
248      * @return the field (if found)
249      * @throws NoSuchFieldException
250      *             if the field cannot be resolved
251      */
252     public static Field resolveField(final Object object, final String fieldName) throws NoSuchFieldException
253     {
254         if (object == null)
255         {
256             throw new NoSuchFieldException("resolveField: object is null for field " + fieldName);
257         }
258         return resolveField(object.getClass(), fieldName);
259     }
260 
261     /*** ************ METHOD UTILITIES *********** */
262     /***
263      * gets all the methods of a class and adds the result to result.
264      * 
265      * @param clazz
266      *            the class
267      * @param name
268      *            the name of the method
269      * @param result
270      *            the resulting set
271      * @return result
272      */
273     public static Method[] getAllMethods(final Class<?> clazz, final String name, final Method[] result)
274     {
275         List<Method> list = new ArrayList<Method>(Arrays.asList(result));
276         Method[] methods = clazz.getDeclaredMethods();
277         for (int i = 0; i < methods.length; i++)
278         {
279             if (methods[i].getName().equals(name))
280             {
281                 list.add(methods[i]);
282             }
283         }
284         if (clazz.getSuperclass() != null)
285         {
286             return ClassUtil
287                     .getAllMethods(clazz.getSuperclass(), name, list.toArray(new Method[list.size()]));
288         }
289         return list.toArray(new Method[list.size()]);
290     }
291 
292     /***
293      * returns the interface method.
294      * 
295      * @param clazz
296      *            the class to start with
297      * @param callerClass
298      *            the caller class
299      * @param name
300      *            the name of the method
301      * @param parameterTypes
302      *            the parameterTypes
303      * @return Method
304      * @throws NoSuchMethodException
305      *             on lookup failure
306      */
307     public static Method resolveMethod(final Class<?> clazz, final Class<?> callerClass, final String name,
308             final Class[] parameterTypes) throws NoSuchMethodException
309     {
310         Method method = ClassUtil.resolveMethod(clazz, name, parameterTypes);
311         if (ClassUtil.isVisible(method, callerClass))
312         {
313             return method;
314         }
315         throw new NoSuchMethodException("method found but not visible");
316     }
317 
318     /***
319      * returns the interface method.
320      * 
321      * @param clazz
322      *            the class to start with
323      * @param name
324      *            the name of the method
325      * @param parameterTypes
326      *            the parameterTypes
327      * @return Method
328      * @throws NoSuchMethodException
329      *             on lookup failure
330      */
331     public static Method resolveMethod(final Class<?> clazz, final String name, final Class[] parameterTypes)
332             throws NoSuchMethodException
333     {
334         try
335         {
336             return resolveMethodSuper(clazz, name, (Class[]) ClassUtil
337                     .checkInput(parameterTypes, Class.class));
338         }
339         catch (Exception exception)
340         {
341             String className = clazz.getName();
342             if (className.indexOf("$") >= 0)
343             {
344                 Class parentClass = null;
345                 try
346                 {
347                     parentClass = Class.forName(className.substring(0, className.lastIndexOf("$")));
348                 }
349                 catch (Exception e2)
350                 {
351                     throw new NoSuchMethodException("class " + parentClass + " not found to resolve method "
352                             + name);
353                 }
354                 return ClassUtil.resolveMethod(parentClass, name, (Class[]) ClassUtil.checkInput(
355                         parameterTypes, Class.class));
356             }
357             throw new NoSuchMethodException("class " + clazz + " does not contain method " + name);
358         }
359     }
360 
361     /***
362      * resolves a method the method.
363      * 
364      * @param object
365      *            the object to start with
366      * @param name
367      *            the name of the method
368      * @param parameterTypes
369      *            the parameterTypes
370      * @return Method
371      * @throws NoSuchMethodException
372      *             on lookup failure
373      */
374     public static Method resolveMethod(final Object object, final String name, final Class[] parameterTypes)
375             throws NoSuchMethodException
376     {
377         if (object == null)
378         {
379             throw new NoSuchMethodException("resolveField: object is null for method " + name);
380         }
381         return resolveMethod(object.getClass(), name, parameterTypes);
382     }
383 
384     /***
385      * returns the method.
386      * 
387      * @param object
388      *            the object to start with
389      * @param name
390      *            the name of the method
391      * @param arguments
392      *            the arguments
393      * @return Method
394      * @throws NoSuchMethodException
395      *             on lookup failure
396      */
397     public static Method resolveMethod(final Object object, final String name, final Object[] arguments)
398             throws NoSuchMethodException
399     {
400         Class[] parameterTypes = ClassUtil.getClass(arguments);
401         String key = "METHOD:" + object.getClass() + "@" + name + "@"
402                 + FieldSignature.toDescriptor(parameterTypes);
403         if (CACHE.containsKey(key))
404         {
405             return (Method) CACHE.get(key);
406         }
407         try
408         {
409             return ClassUtil.resolveMethod(object, name, parameterTypes);
410         }
411         catch (NoSuchMethodException noSuchMethodException)
412         {
413             // We get all methods
414             Method[] methods = ClassUtil.getAllMethods(object.getClass(), name, new Method[0]);
415             if (methods.length == 0)
416             {
417                 throw new NoSuchMethodException("No such method: " + name + " for object " + object);
418             }
419             // now we match the signatures
420             methods = ClassUtil.matchSignature(methods, name, parameterTypes);
421             if (methods.length == 0)
422             {
423                 throw new NoSuchMethodException("No method with right signature: " + name + " for object "
424                         + object);
425             }
426             // Now we find the most specific
427             Method result = ClassUtil.getSpecificMethod(methods);
428             CACHE.put(key, result);
429             return result;
430         }
431     }
432 
433     /***
434      * Returns whether a declaringClass is accessible according to the modifiers.
435      * 
436      * @param modifiers
437      *            the modifiers
438      * @param declaringClass
439      *            the declaringClass
440      * @param caller
441      *            the caller
442      * @return boolean isVisible
443      */
444     public static boolean isVisible(final int modifiers, final Class<?> declaringClass, final Class<?> caller)
445     {
446         if (Modifier.isPublic(modifiers))
447         {
448             return true;
449         }
450         if (Modifier.isProtected(modifiers))
451         {
452             if (declaringClass.isAssignableFrom(caller))
453             {
454                 return true;
455             }
456             if (declaringClass.getPackage().equals(caller.getPackage()))
457             {
458                 return true;
459             }
460             return false;
461         }
462         if (declaringClass.equals(caller))
463         {
464             return true;
465         }
466         return false;
467     }
468 
469     /***
470      * Determines & returns whether constructor 'a' is more specific than constructor 'b', as defined in the
471      * Java Language Specification ???15.12.
472      * 
473      * @return true if 'a' is more specific than b, false otherwise. 'false' is also returned when
474      *         constructors are incompatible, e.g. have different names or a different number of parameters.
475      * @param a
476      *            reflects the first constructor
477      * @param b
478      *            reflects the second constructor
479      */
480     public static boolean isMoreSpecific(final Class<?>[] a, final Class<?>[] b)
481     {
482         if (a.length != b.length)
483         {
484             return false;
485         }
486         int i = 0;
487         while (i < a.length)
488         {
489             if (!b[i].isAssignableFrom(a[i]))
490             {
491                 return false;
492             }
493             i++;
494         }
495         return true;
496     }
497 
498     /***
499      * Determines & returns whether constructor 'a' is more specific than constructor 'b', as defined in the
500      * Java Language Specification ???15.12.
501      * 
502      * @return true if 'a' is more specific than b, false otherwise. 'false' is also returned when
503      *         constructors are incompatible, e.g. have different names or a different number of parameters.
504      * @param a
505      *            reflects the first constructor
506      * @param b
507      *            reflects the second constructor
508      */
509     public static boolean isMoreSpecific(final Constructor<?> a, final Constructor<?> b)
510     {
511         if (a.getParameterTypes().equals(b.getParameterTypes()))
512         {
513             if (b.getDeclaringClass().isAssignableFrom(a.getDeclaringClass()))
514             {
515                 return true;
516             }
517         }
518         return ClassUtil.isMoreSpecific(a.getParameterTypes(), b.getParameterTypes());
519     }
520 
521     /***
522      * Determines & returns whether constructor 'a' is more specific than constructor 'b', as defined in the
523      * Java Language Specification ???15.12.
524      * 
525      * @return true if 'a' is more specific than b, false otherwise. 'false' is also returned when
526      *         constructors are incompatible, e.g. have different names or a different number of parameters.
527      * @param a
528      *            reflects the first method
529      * @param b
530      *            reflects the second method
531      */
532     public static boolean isMoreSpecific(final Method a, final Method b)
533     {
534         if (!a.getName().equals(b.getName()))
535         {
536             return false;
537         }
538         return ClassUtil.isMoreSpecific(a.getParameterTypes(), b.getParameterTypes());
539     }
540 
541     /***
542      * Returns whether a field is visible for a caller.
543      * 
544      * @param field
545      *            The field
546      * @param caller
547      *            The class of the caller for whom invocation visibility is checked.
548      * @return boolean yes or no
549      */
550     public static boolean isVisible(final Field field, final Class<?> caller)
551     {
552         return ClassUtil.isVisible(field.getModifiers(), field.getDeclaringClass(), caller);
553     }
554 
555     /***
556      * Returns whether a constructor is visible for a caller.
557      * 
558      * @param constructor
559      *            The constructor
560      * @param caller
561      *            The class of the caller for whom invocation visibility is checked.
562      * @return boolean yes or no
563      */
564     public static boolean isVisible(final Constructor<?> constructor, final Class<?> caller)
565     {
566         return ClassUtil.isVisible(constructor.getModifiers(), constructor.getDeclaringClass(), caller);
567     }
568 
569     /***
570      * Returns whether a method is visible for a caller.
571      * 
572      * @param method
573      *            The method
574      * @param caller
575      *            The class of the caller for whom invocation visibility is checked.
576      * @return boolean yes or no
577      */
578     public static boolean isVisible(final Method method, final Class<?> caller)
579     {
580         return ClassUtil.isVisible(method.getModifiers(), method.getDeclaringClass(), caller);
581     }
582 
583     /***
584      * Filters an array methods for signatures that are compatible with a given signature.
585      * 
586      * @param methods
587      *            which are methods to be filtered.
588      * @param name
589      *            reflects the method's name, part of the signature
590      * @param argTypes
591      *            are the method's argument types
592      * @return Method[] An unordered Method-array consisting of the elements of 'methods' that match with the
593      *         given signature. An array with 0 elements is returned when no matching Method objects are
594      *         found.
595      */
596     public static Method[] matchSignature(final Method[] methods, final String name, final Class[] argTypes)
597     {
598         List<Method> results = new ArrayList<Method>();
599         for (int i = 0; i < methods.length; i++)
600         {
601             if (ClassUtil.matchSignature(methods[i], name, argTypes))
602             {
603                 results.add(methods[i]);
604             }
605         }
606         return results.toArray(new Method[results.size()]);
607     }
608 
609     /***
610      * Filters an array methods for signatures that are compatible with a given signature.
611      * 
612      * @param method
613      *            The method to be filtered.
614      * @param name
615      *            reflects the method's name, part of the signature
616      * @param argTypes
617      *            are the method's argument types
618      * @return boolean if methodParameters assignable from argTypes
619      */
620     public static boolean matchSignature(final Method method, final String name, final Class[] argTypes)
621     {
622         if (!method.getName().equals(name))
623         {
624             return false;
625         }
626         if (method.getParameterTypes().length != argTypes.length)
627         {
628             return false;
629         }
630         Class<?>[] types = method.getParameterTypes();
631         for (int i = 0; i < method.getParameterTypes().length; i++)
632         {
633             if (!(types[i].isAssignableFrom(argTypes[i]) || types[i].equals(Primitive
634                     .getPrimitive(argTypes[i]))))
635             {
636                 return false;
637             }
638         }
639         return true;
640     }
641 
642     /***
643      * Filters an array methods for signatures that are compatible with a given signature.
644      * 
645      * @param constructor
646      *            which are constructors to be filtered.
647      * @param argTypes
648      *            are the constructor's argument types
649      * @return boolean if methodParameters assignable from argTypes
650      */
651     public static boolean matchSignature(final Constructor<?> constructor, final Class[] argTypes)
652     {
653         if (constructor.getParameterTypes().length != argTypes.length)
654         {
655             return false;
656         }
657         Class<?>[] types = constructor.getParameterTypes();
658         for (int i = 0; i < constructor.getParameterTypes().length; i++)
659         {
660             if (!(types[i].isAssignableFrom(argTypes[i]) || types[i].equals(Primitive
661                     .getPrimitive(argTypes[i]))))
662             {
663                 return false;
664             }
665         }
666         return true;
667     }
668 
669     /***
670      * Filters an array methods for signatures that are compatible with a given signature.
671      * 
672      * @param constructors
673      *            which are constructors to be filtered.
674      * @param argTypes
675      *            are the constructor's argument types
676      * @return Constructor[] An unordered Constructor-array consisting of the elements of 'constructors' that
677      *         match with the given signature. An array with 0 elements is returned when no matching Method
678      *         objects are found.
679      */
680     public static Constructor[] matchSignature(final Constructor[] constructors, final Class[] argTypes)
681     {
682         List<Constructor> results = new ArrayList<Constructor>();
683         for (int i = 0; i < constructors.length; i++)
684         {
685             if (ClassUtil.matchSignature(constructors[i], argTypes))
686             {
687                 results.add(constructors[i]);
688             }
689         }
690         return results.toArray(new Constructor[results.size()]);
691     }
692 
693     /***
694      * converts an array of objects to their corresponding classes
695      * 
696      * @param array
697      *            the array to invoke
698      * @return Class[] the result;
699      */
700     public static Class[] getClass(final Object[] array)
701     {
702         if (array == null)
703         {
704             return new Class[0];
705         }
706         Class[] result = new Class[array.length];
707         for (int i = 0; i < result.length; i++)
708         {
709             if (array[i] == null)
710             {
711                 result[i] = null;
712             }
713             else
714             {
715                 result[i] = array[i].getClass();
716             }
717         }
718         return result;
719     }
720 
721     /*** ************** PRIVATE METHODS ********* */
722     /***
723      * checks the input of an array
724      * 
725      * @param array
726      *            the array
727      * @param myClass
728      *            the class of the result
729      * @return Returns array if array!=null else returns myClass[0]
730      */
731     private static Object checkInput(final Object[] array, final Class myClass)
732     {
733         if (array != null)
734         {
735             return array;
736         }
737         return Array.newInstance(myClass, 0);
738     }
739 
740     /***
741      * Determines & returns the most specific constructor as defined in the Java Language Specification
742      * ???15.12. The current algorithm is simple and reliable, but probably slow.
743      * 
744      * @param methods
745      *            are the constructors to be searched. They are assumed to have the same name and number of
746      *            parameters, as determined by the constructor matchSignature.
747      * @return Constructor which is the most specific constructor.
748      * @throws NoSuchMethodException
749      *             when no constructor is found that's more specific than the others.
750      */
751     private static Constructor getSpecificConstructor(final Constructor[] methods)
752             throws NoSuchMethodException
753     {
754         if (methods.length == 0)
755         {
756             throw new NoSuchMethodException();
757         }
758         if (methods.length == 1)
759         {
760             return methods[0];
761         }
762         // Apply generic algorithm
763         int resultID = 0; // Assume first method to be most specific
764         while (resultID < methods.length)
765         {
766             // Verify assumption
767             boolean success = true;
768             for (int i = 0; i < methods.length; i++)
769             {
770                 if (resultID == i)
771                 {
772                     continue;
773                 }
774                 if (!isMoreSpecific(methods[resultID], methods[i]))
775                 {
776                     success = false;
777                 }
778             }
779             // Assumption verified
780             if (success)
781             {
782                 return methods[resultID];
783             }
784             resultID++;
785         }
786         // No method is most specific, thus:
787         throw new NoSuchMethodException();
788     }
789 
790     /***
791      * Determines & returns the most specific method as defined in the Java Language Specification ???15.12.
792      * The current algorithm is simple and reliable, but probably slow.
793      * 
794      * @param methods
795      *            which are the methods to be searched. They are assumed to have the same name and number of
796      *            parameters, as determined by the method matchSignature.
797      * @return The most specific method.
798      * @throws NoSuchMethodException
799      *             when no method is found that's more specific than the others.
800      */
801     private static Method getSpecificMethod(final Method[] methods) throws NoSuchMethodException
802     {
803         // Check for evident cases
804         if (methods.length == 0)
805         {
806             throw new NoSuchMethodException();
807         }
808         if (methods.length == 1)
809         {
810             return methods[0];
811         }
812         // Apply generic algorithm
813         int resultID = 0; // Assume first method to be most specific
814         while (resultID < methods.length)
815         {
816             // Verify assumption
817             boolean success = true;
818             for (int i = 0; i < methods.length; i++)
819             {
820                 if (resultID == i)
821                 {
822                     continue;
823                 }
824                 if (!isMoreSpecific(methods[resultID], methods[i]))
825                 {
826                     success = false;
827                 }
828             }
829             // Assumption verified
830             if (success)
831             {
832                 return methods[resultID];
833             }
834             resultID++;
835         }
836         // No method is most specific, thus:
837         throw new NoSuchMethodException();
838     }
839 
840     /***
841      * returns the constructor
842      * 
843      * @param clazz
844      *            the class to start with
845      * @param parameterTypes
846      *            the parameterTypes
847      * @return Method
848      * @throws NoSuchMethodException
849      *             if the method cannot be resolved
850      */
851     private static Constructor<?> resolveConstructorSuper(final Class<?> clazz, final Class[] parameterTypes)
852             throws NoSuchMethodException
853     {
854         try
855         {
856             String key = "CONSTRUCTOR:" + clazz + "@" + FieldSignature.toDescriptor(parameterTypes);
857             if (CACHE.containsKey(key))
858             {
859                 return (Constructor<?>) CACHE.get(key);
860             }
861             Constructor<?> constructor = clazz.getDeclaredConstructor(parameterTypes);
862             CACHE.put(key, constructor);
863             return constructor;
864         }
865         catch (Exception exception)
866         {
867             if (clazz.getSuperclass() != null)
868             {
869                 return ClassUtil.resolveConstructorSuper(clazz.getSuperclass(), parameterTypes);
870             }
871             throw new NoSuchMethodException(exception.getMessage());
872         }
873     }
874 
875     /***
876      * returns the interface method
877      * 
878      * @param clazz
879      *            the class to start with
880      * @param name
881      *            the name of the method
882      * @param parameterTypes
883      *            the parameterTypes
884      * @return Method
885      * @throws NoSuchMethodException
886      *             on lookup failure
887      */
888     private static <T> Method resolveMethodSuper(final Class<T> clazz, final String name,
889             final Class[] parameterTypes) throws NoSuchMethodException
890     {
891         try
892         {
893             String key = "METHOD:" + clazz + "@" + name + "@" + FieldSignature.toDescriptor(parameterTypes);
894             if (CACHE.containsKey(key))
895             {
896                 return (Method) CACHE.get(key);
897             }
898             Method method = clazz.getDeclaredMethod(name, parameterTypes);
899             CACHE.put(key, method);
900             return method;
901         }
902         catch (Exception exception)
903         {
904             if (clazz.getSuperclass() != null)
905             {
906                 return ClassUtil.resolveMethodSuper(clazz.getSuperclass(), name, parameterTypes);
907             }
908             throw new NoSuchMethodException(exception.getMessage());
909         }
910     }
911 
912     /***
913      * resolves the field for a class, taking into account superclasses
914      * 
915      * @param clazz
916      *            the class for which superclasses will be probed
917      * @param fieldName
918      *            the name of the field to resolve
919      * @return the field (if found)
920      * @throws NoSuchFieldException
921      *             if the field cannot be resolved
922      */
923     private static <T> Field resolveFieldSuper(final Class<T> clazz, final String fieldName)
924             throws NoSuchFieldException
925     {
926         try
927         {
928             if (CACHE.containsKey("FIELD:" + clazz + "@" + fieldName))
929             {
930                 return (Field) CACHE.get("FIELD:" + clazz + "@" + fieldName);
931             }
932             Field result = clazz.getDeclaredField(fieldName);
933             CACHE.put("FIELD:" + clazz + "@" + fieldName, result);
934             return result;
935         }
936         catch (Exception e)
937         {
938             if (clazz.getSuperclass() != null)
939             {
940                 return ClassUtil.resolveFieldSuper(clazz.getSuperclass(), fieldName);
941             }
942             throw new NoSuchFieldException("class " + clazz + " does not contain field " + fieldName);
943         }
944     }
945 }