View Javadoc

1   /*
2    * @(#)MethodUtils.java Feb 1, 2003
3    * 
4    * Copyright (c) 2003 Delft University of Technology Jaffalaan 5, 2628 BX Delft,
5    * the Netherlands All rights reserved.
6    * 
7    * This software is proprietary information of Delft University of Technology
8    * The code is published under the General Public License
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  		//unreachable code
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 			//We get all constructors
163 			Constructor[] constructors = ClassUtil.getAllConstructors(clazz,
164 					new Constructor[0]);
165 			//now we match the signatures
166 			constructors = ClassUtil.matchSignature(constructors,
167 					parameterTypes);
168 			//Now we find the most specific
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 			//	We get all methods
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 			//now we match the signatures
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 			//Now we find the most specific
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 		// Apply generic algorithm
735 		int resultID = 0; // Assume first method to be most specific
736 		while (resultID < methods.length)
737 		{
738 			// Verify assumption
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 			// Assumption verified
752 			if (success)
753 			{
754 				return methods[resultID];
755 			}
756 			resultID++;
757 		}
758 		// No method is most specific, thus:
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 		// Check for evident cases
778 		if (methods.length == 0)
779 		{
780 			throw new NoSuchMethodException();
781 		}
782 		if (methods.length == 1)
783 		{
784 			return methods[0];
785 		}
786 		// Apply generic algorithm
787 		int resultID = 0; // Assume first method to be most specific
788 		while (resultID < methods.length)
789 		{
790 			// Verify assumption
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 			// Assumption verified
804 			if (success)
805 			{
806 				return methods[resultID];
807 			}
808 			resultID++;
809 		}
810 		// No method is most specific, thus:
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 }