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