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