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
21
22
23
24
25
26
27
28
29 public final class ClassUtil
30 {
31
32 private static final Map<String, Object> CACHE = Collections.synchronizedMap(new HashMap<String, Object>());
33
34
35
36
37 private ClassUtil()
38 {
39 super();
40
41 }
42
43
44
45
46
47
48
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
63
64
65
66
67
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
82
83
84
85
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
117
118
119
120
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
138 Constructor<?>[] constructors = ClassUtil.getAllConstructors(clazz, new Constructor[0]);
139
140 constructors = ClassUtil.matchSignature(constructors, parameterTypes);
141
142 Constructor<?> result = ClassUtil.getSpecificConstructor(constructors);
143 CACHE.put(key, result);
144 return result;
145 }
146 }
147
148
149
150
151
152
153
154
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
172
173
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
183
184
185
186
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
217
218
219
220
221
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
236
237
238
239
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
251
252
253
254
255
256
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
278
279
280
281
282
283
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
298
299
300
301
302
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
334
335
336
337
338
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
352
353
354
355
356
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
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
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
386 Method result = ClassUtil.getSpecificMethod(methods);
387 CACHE.put(key, result);
388 return result;
389 }
390 }
391
392
393
394
395
396
397
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
426
427
428
429
430
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
452
453
454
455
456
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
472
473
474
475
476
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
489
490
491
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
500
501
502
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
511
512
513
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
522
523
524
525
526
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
543
544
545
546
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
571
572
573
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
594
595
596
597
598
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
615
616
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
640
641
642
643
644
645
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
658
659
660
661
662
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
675 int resultID = 0;
676 while (resultID < methods.length)
677 {
678
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
692 if (success)
693 {
694 return methods[resultID];
695 }
696 resultID++;
697 }
698
699 throw new NoSuchMethodException();
700 }
701
702
703
704
705
706
707
708
709
710 private static Method getSpecificMethod(final Method[] methods) throws NoSuchMethodException
711 {
712
713 if (methods.length == 0)
714 {
715 throw new NoSuchMethodException();
716 }
717 if (methods.length == 1)
718 {
719 return methods[0];
720 }
721
722 int resultID = 0;
723 while (resultID < methods.length)
724 {
725
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
739 if (success)
740 {
741 return methods[resultID];
742 }
743 resultID++;
744 }
745
746 throw new NoSuchMethodException();
747 }
748
749
750
751
752
753
754
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
784
785
786
787
788
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
818
819
820
821
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 }