1
2
3
4
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
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
167 Constructor[] constructors = ClassUtil.getAllConstructors(clazz, new Constructor[0]);
168
169 constructors = ClassUtil.matchSignature(constructors, parameterTypes);
170
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
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
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
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
763 int resultID = 0;
764 while (resultID < methods.length)
765 {
766
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
780 if (success)
781 {
782 return methods[resultID];
783 }
784 resultID++;
785 }
786
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
804 if (methods.length == 0)
805 {
806 throw new NoSuchMethodException();
807 }
808 if (methods.length == 1)
809 {
810 return methods[0];
811 }
812
813 int resultID = 0;
814 while (resultID < methods.length)
815 {
816
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
830 if (success)
831 {
832 return methods[resultID];
833 }
834 resultID++;
835 }
836
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 }