1 package org.djutils.reflection;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.lang.annotation.Annotation;
6 import java.lang.reflect.Array;
7 import java.lang.reflect.Constructor;
8 import java.lang.reflect.Field;
9 import java.lang.reflect.Method;
10 import java.lang.reflect.Modifier;
11 import java.lang.reflect.Proxy;
12 import java.net.URISyntaxException;
13 import java.net.URL;
14 import java.nio.file.Files;
15 import java.nio.file.Paths;
16 import java.nio.file.attribute.BasicFileAttributes;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.NoSuchElementException;
25 import java.util.Set;
26 import java.util.jar.JarEntry;
27 import java.util.jar.JarFile;
28 import java.util.zip.ZipEntry;
29
30 import org.djutils.io.URLResource;
31 import org.djutils.primitives.Primitive;
32
33
34
35
36
37
38
39
40
41
42
43 public final class ClassUtil
44 {
45
46 private static final Map<String, Object> CACHE = Collections.synchronizedMap(new HashMap<String, Object>());
47
48
49
50
51 private ClassUtil()
52 {
53 super();
54
55 }
56
57
58
59
60
61
62
63
64
65
66 @SuppressWarnings("unchecked")
67 public static <T> Constructor<T>[] getAllConstructors(final Class<T> clazz)
68 {
69 return (Constructor<T>[]) clazz.getDeclaredConstructors();
70 }
71
72
73
74
75
76
77
78
79
80 @SuppressWarnings("unchecked")
81 private static <T> Constructor<T> resolveConstructorCache(final Class<T> clazz, final Class<?>[] parameterTypes)
82 throws NoSuchMethodException
83 {
84 String key = "CONSTRUCTOR:" + clazz + "@" + FieldSignature.toDescriptor(parameterTypes);
85 if (CACHE.containsKey(key))
86 {
87 return (Constructor<T>) CACHE.get(key);
88 }
89 Constructor<T> constructor = clazz.getDeclaredConstructor(parameterTypes);
90 CACHE.put(key, constructor);
91 return constructor;
92 }
93
94
95
96
97
98
99
100
101
102
103
104
105
106 public static <T> Constructor<T> resolveConstructor(final Class<T> clazz, final Class<?> callerClass,
107 final Class<?>[] parameterTypes) throws NoSuchMethodException, IllegalAccessException
108 {
109 Constructor<T> constructor = ClassUtil.resolveConstructor(clazz, parameterTypes);
110 if (ClassUtil.isVisible(constructor, callerClass.getClass()))
111 {
112 return constructor;
113 }
114 throw new IllegalAccessException("constructor resolved but not visible");
115 }
116
117
118
119
120
121
122
123
124 @SuppressWarnings("unchecked")
125 public static <T> Constructor<T> resolveConstructor(final Class<T> clazz, final Class<?>[] parameterTypes)
126 throws NoSuchMethodException
127 {
128 try
129 {
130 return resolveConstructorCache(clazz, (Class<?>[]) ClassUtil.checkInput(parameterTypes, Class.class));
131 }
132 catch (Exception exception)
133 {
134 String className = clazz.getName();
135 if (className.indexOf("$") >= 0)
136 {
137 Class<?> parentClass = null;
138 try
139 {
140 parentClass = Class.forName(className.substring(0, className.lastIndexOf("$")));
141 }
142 catch (Exception e2)
143 {
144 throw new NoSuchMethodException("class " + parentClass + " not found to resolve constructor");
145 }
146 return (Constructor<T>) ClassUtil.resolveConstructor(parentClass,
147 (Class<?>[]) ClassUtil.checkInput(parameterTypes, Class.class));
148 }
149 throw new NoSuchMethodException("class " + clazz + " does not contain constructor");
150 }
151 }
152
153
154
155
156
157
158
159
160 @SuppressWarnings("unchecked")
161 public static <T> Constructor<T> resolveConstructor(final Class<T> clazz, final Object[] arguments)
162 throws NoSuchMethodException
163 {
164 Class<?>[] parameterTypes = ClassUtil.getClass(arguments);
165 String key = "CONSTRUCTOR:" + clazz + "@" + FieldSignature.toDescriptor(parameterTypes);
166 if (CACHE.containsKey(key))
167 {
168 return (Constructor<T>) CACHE.get(key);
169 }
170 try
171 {
172 return ClassUtil.resolveConstructor(clazz, parameterTypes);
173 }
174 catch (NoSuchMethodException noSuchMethodException)
175 {
176
177 Constructor<T>[] constructors = ClassUtil.getAllConstructors(clazz);
178
179 constructors = ClassUtil.matchSignature(constructors, parameterTypes);
180
181 Constructor<T> result = (Constructor<T>) ClassUtil.getSpecificConstructor(constructors);
182 CACHE.put(key, result);
183 return result;
184 }
185 }
186
187
188
189
190
191
192
193 public static <T> boolean matchSignature(final Constructor<T> constructor, final Class<?>[] argTypes)
194 {
195 if (constructor.getParameterTypes().length != argTypes.length)
196 {
197 return false;
198 }
199 Class<?>[] types = constructor.getParameterTypes();
200 for (int i = 0; i < constructor.getParameterTypes().length; i++)
201 {
202 if (!(types[i].isAssignableFrom(argTypes[i]) || types[i].equals(Primitive.getPrimitive(argTypes[i]))))
203 {
204 return false;
205 }
206 }
207 return true;
208 }
209
210
211
212
213
214
215
216
217 @SuppressWarnings("unchecked")
218 public static <T> Constructor<T>[] matchSignature(final Constructor<T>[] constructors, final Class<?>[] argTypes)
219 {
220 List<Constructor<T>> results = new ArrayList<Constructor<T>>();
221 for (int i = 0; i < constructors.length; i++)
222 {
223 if (ClassUtil.matchSignature(constructors[i], argTypes))
224 {
225 results.add(constructors[i]);
226 }
227 }
228 return results.toArray(new Constructor[results.size()]);
229 }
230
231
232
233
234
235
236
237
238
239 public static Set<Field> getAllFields(final Class<?> clazz, final Set<Field> result)
240 {
241 Field[] fields = clazz.getDeclaredFields();
242 for (int i = 0; i < fields.length; i++)
243 {
244 result.add(fields[i]);
245 }
246 if (clazz.getSuperclass() != null)
247 {
248 return ClassUtil.getAllFields(clazz.getSuperclass(), result);
249 }
250 return result;
251 }
252
253
254
255
256
257
258 public static Set<Field> getAllFields(final Class<?> clazz)
259 {
260 Set<Field> fieldSet = new HashSet<Field>();
261 return ClassUtil.getAllFields(clazz, fieldSet);
262 }
263
264
265
266
267
268
269
270
271
272 public static Field resolveField(final Class<?> clazz, final String fieldName) throws NoSuchFieldException
273 {
274 try
275 {
276 return resolveFieldSuper(clazz, fieldName);
277 }
278 catch (NoSuchFieldException noSuchFieldException)
279 {
280 String className = clazz.getName();
281 if (className.indexOf("$") >= 0)
282 {
283 Class<?> clazz2 = null;
284 try
285 {
286 clazz2 = Class.forName(className.substring(0, className.lastIndexOf("$")));
287 }
288 catch (ClassNotFoundException classNotFoundException)
289 {
290 throw new NoSuchFieldException("class " + clazz + " not found to resolve field " + fieldName);
291 }
292 return ClassUtil.resolveField(clazz2, fieldName);
293 }
294 throw new NoSuchFieldException("class " + clazz + " does not contain field " + fieldName);
295 }
296 }
297
298
299
300
301
302
303
304
305
306 public static Field resolveField(final Class<?> clazz, final Class<?> callerClass, final String name)
307 throws NoSuchFieldException
308 {
309 Field field = ClassUtil.resolveField(clazz, name);
310 if (ClassUtil.isVisible(field, callerClass.getClass()))
311 {
312 return field;
313 }
314 throw new NoSuchFieldException("field resolved but not visible");
315 }
316
317
318
319
320
321
322
323
324 public static Field resolveField(final Object object, final String fieldName) throws NoSuchFieldException
325 {
326 if (object == null)
327 {
328 throw new NoSuchFieldException("resolveField: object is null for field " + fieldName);
329 }
330 return resolveField(object.getClass(), fieldName);
331 }
332
333
334
335
336
337
338
339
340
341 public static Method[] getAllMethods(final Class<?> clazz, final String name, final Method[] result)
342 {
343 List<Method> list = new ArrayList<Method>(Arrays.asList(result));
344 Method[] methods = clazz.getDeclaredMethods();
345 for (int i = 0; i < methods.length; i++)
346 {
347 if (methods[i].getName().equals(name))
348 {
349 list.add(methods[i]);
350 }
351 }
352 if (clazz.getSuperclass() != null)
353 {
354 return ClassUtil.getAllMethods(clazz.getSuperclass(), name, list.toArray(new Method[list.size()]));
355 }
356 return list.toArray(new Method[list.size()]);
357 }
358
359
360
361
362
363
364
365
366
367
368 public static Method resolveMethod(final Class<?> clazz, final Class<?> callerClass, final String name,
369 final Class<?>[] parameterTypes) throws NoSuchMethodException
370 {
371 Method method = ClassUtil.resolveMethod(clazz, name, parameterTypes);
372 if (ClassUtil.isVisible(method, callerClass))
373 {
374 return method;
375 }
376 throw new NoSuchMethodException("method found but not visible");
377 }
378
379
380
381
382
383
384
385
386
387 public static Method resolveMethod(final Class<?> clazz, final String name, final Class<?>[] parameterTypes)
388 throws NoSuchMethodException
389 {
390 try
391 {
392 return resolveMethodSuper(clazz, name, (Class<?>[]) ClassUtil.checkInput(parameterTypes, Class.class));
393 }
394 catch (Exception exception)
395 {
396 String className = clazz.getName();
397 if (className.indexOf("$") >= 0)
398 {
399 Class<?> parentClass = null;
400 try
401 {
402 parentClass = Class.forName(className.substring(0, className.lastIndexOf("$")));
403 }
404 catch (Exception e2)
405 {
406 throw new NoSuchMethodException("class " + parentClass + " not found to resolve method " + name);
407 }
408 return ClassUtil.resolveMethod(parentClass, name,
409 (Class<?>[]) ClassUtil.checkInput(parameterTypes, Class.class));
410 }
411 throw new NoSuchMethodException("class " + clazz + " does not contain method " + name);
412 }
413 }
414
415
416
417
418
419
420
421
422
423 public static Method resolveMethod(final Object object, final String name, final Class<?>[] parameterTypes)
424 throws NoSuchMethodException
425 {
426 if (object == null)
427 {
428 throw new NoSuchMethodException("resolveField: object is null for method " + name);
429 }
430 return resolveMethod(object.getClass(), name, parameterTypes);
431 }
432
433
434
435
436
437
438
439
440
441 public static Method resolveMethod(final Object object, final String name, final Object[] arguments)
442 throws NoSuchMethodException
443 {
444 Class<?>[] parameterTypes = ClassUtil.getClass(arguments);
445 String key = "METHOD:" + object.getClass() + "@" + name + "@" + FieldSignature.toDescriptor(parameterTypes);
446 if (CACHE.containsKey(key))
447 {
448 return (Method) CACHE.get(key);
449 }
450 try
451 {
452 return ClassUtil.resolveMethod(object, name, parameterTypes);
453 }
454 catch (NoSuchMethodException noSuchMethodException)
455 {
456
457 Method[] methods = ClassUtil.getAllMethods(object.getClass(), name, new Method[0]);
458 if (methods.length == 0)
459 {
460 throw new NoSuchMethodException("No such method: " + name + " for object " + object);
461 }
462
463 methods = ClassUtil.matchSignature(methods, name, parameterTypes);
464 if (methods.length == 0)
465 {
466 throw new NoSuchMethodException("No method with right signature: " + name + " for object " + object);
467 }
468
469 Method result = ClassUtil.getSpecificMethod(methods);
470 CACHE.put(key, result);
471 return result;
472 }
473 }
474
475
476
477
478
479
480
481
482
483 public static Set<Annotation> getAllAnnotations(final Class<?> clazz, final Set<Annotation> result)
484 {
485 Annotation[] annotations = clazz.getDeclaredAnnotations();
486 for (int i = 0; i < annotations.length; i++)
487 {
488 result.add(annotations[i]);
489 }
490 if (clazz.getSuperclass() != null)
491 {
492 return ClassUtil.getAllAnnotations(clazz.getSuperclass(), result);
493 }
494 return result;
495 }
496
497
498
499
500
501
502 public static Set<Annotation> getAllAnnotations(final Class<?> clazz)
503 {
504 Set<Annotation> annotationSet = new HashSet<Annotation>();
505 return ClassUtil.getAllAnnotations(clazz, annotationSet);
506 }
507
508
509
510
511
512
513
514
515
516 public static Annotation resolveAnnotation(final Class<?> clazz, final Class<? extends Annotation> annotationClass)
517 throws NoSuchElementException
518 {
519 try
520 {
521 return resolveAnnotationSuper(clazz, annotationClass);
522 }
523 catch (NoSuchElementException noSuchAnnotationException)
524 {
525 String className = clazz.getName();
526 if (className.indexOf("$") >= 0)
527 {
528 Class<?> clazz2 = null;
529 try
530 {
531 clazz2 = Class.forName(className.substring(0, className.lastIndexOf("$")));
532 }
533 catch (ClassNotFoundException classNotFoundException)
534 {
535 throw new NoSuchElementException("class " + clazz + " not found to resolve annotation " + annotationClass);
536 }
537 return ClassUtil.resolveAnnotation(clazz2, annotationClass);
538 }
539 throw new NoSuchElementException("class " + clazz + " does not contain annotation " + annotationClass);
540 }
541 }
542
543
544
545
546
547
548
549
550 public static Annotation resolveAnnotation(final Object object, final String annotationName) throws NoSuchElementException
551 {
552 if (object == null)
553 {
554 throw new NoSuchElementException("resolveAnnotation: object is null for annotation " + annotationName);
555 }
556 return resolveAnnotation(object.getClass(), annotationName);
557 }
558
559
560
561
562
563
564
565
566
567
568 public static boolean isVisible(final int modifiers, final Class<?> declaringClass, final Class<?> caller)
569 {
570 if (Modifier.isPublic(modifiers))
571 {
572 return true;
573 }
574 if (Modifier.isProtected(modifiers))
575 {
576 if (declaringClass.isAssignableFrom(caller))
577 {
578 return true;
579 }
580 if (declaringClass.getPackage().equals(caller.getPackage()))
581 {
582 return true;
583 }
584 return false;
585 }
586 if (declaringClass.equals(caller))
587 {
588 return true;
589 }
590 return false;
591 }
592
593
594
595
596
597
598
599
600
601 public static boolean isMoreSpecific(final Class<?>[] a, final Class<?>[] b)
602 {
603 if (a.length != b.length)
604 {
605 return false;
606 }
607 int i = 0;
608 while (i < a.length)
609 {
610 if (!b[i].isAssignableFrom(a[i]))
611 {
612 return false;
613 }
614 i++;
615 }
616 return true;
617 }
618
619
620
621
622
623
624
625
626
627 public static boolean isMoreSpecific(final Constructor<?> a, final Constructor<?> b)
628 {
629 if (a.getParameterTypes().equals(b.getParameterTypes()))
630 {
631 if (b.getDeclaringClass().isAssignableFrom(a.getDeclaringClass()))
632 {
633 return true;
634 }
635 }
636 return ClassUtil.isMoreSpecific(a.getParameterTypes(), b.getParameterTypes());
637 }
638
639
640
641
642
643
644
645
646
647 public static boolean isMoreSpecific(final Method a, final Method b)
648 {
649 if (!a.getName().equals(b.getName()))
650 {
651 return false;
652 }
653 return ClassUtil.isMoreSpecific(a.getParameterTypes(), b.getParameterTypes());
654 }
655
656
657
658
659
660
661
662 public static boolean isVisible(final Field field, final Class<?> caller)
663 {
664 return ClassUtil.isVisible(field.getModifiers(), field.getDeclaringClass(), caller);
665 }
666
667
668
669
670
671
672
673 public static boolean isVisible(final Constructor<?> constructor, final Class<?> caller)
674 {
675 return ClassUtil.isVisible(constructor.getModifiers(), constructor.getDeclaringClass(), caller);
676 }
677
678
679
680
681
682
683
684 public static boolean isVisible(final Method method, final Class<?> caller)
685 {
686 return ClassUtil.isVisible(method.getModifiers(), method.getDeclaringClass(), caller);
687 }
688
689
690
691
692
693
694
695
696
697 public static Method[] matchSignature(final Method[] methods, final String name, final Class<?>[] argTypes)
698 {
699 List<Method> results = new ArrayList<Method>();
700 for (int i = 0; i < methods.length; i++)
701 {
702 if (ClassUtil.matchSignature(methods[i], name, argTypes))
703 {
704 results.add(methods[i]);
705 }
706 }
707 return results.toArray(new Method[results.size()]);
708 }
709
710
711
712
713
714
715
716
717 public static boolean matchSignature(final Method method, final String name, final Class<?>[] argTypes)
718 {
719 if (!method.getName().equals(name))
720 {
721 return false;
722 }
723 if (method.getParameterTypes().length != argTypes.length)
724 {
725 return false;
726 }
727 Class<?>[] types = method.getParameterTypes();
728 for (int i = 0; i < method.getParameterTypes().length; i++)
729 {
730 if (!(types[i].isAssignableFrom(argTypes[i]) || types[i].equals(Primitive.getPrimitive(argTypes[i]))))
731 {
732 return false;
733 }
734 }
735 return true;
736 }
737
738
739
740
741
742
743
744
745 public static Class<?>[] getClass(final Object[] array)
746 {
747 if (array == null)
748 {
749 return new Class[0];
750 }
751 Class<?>[] result = new Class[array.length];
752 for (int i = 0; i < result.length; i++)
753 {
754 if (array[i] == null)
755 {
756 result[i] = null;
757 }
758 else
759 {
760 result[i] = array[i].getClass();
761 }
762 }
763 return result;
764 }
765
766
767
768
769
770
771
772
773
774 private static Object checkInput(final Object[] array, final Class<?> myClass)
775 {
776 if (array != null)
777 {
778 return array;
779 }
780 return Array.newInstance(myClass, 0);
781 }
782
783
784
785
786
787
788
789
790
791 private static Constructor<?> getSpecificConstructor(final Constructor<?>[] methods) throws NoSuchMethodException
792 {
793 if (methods.length == 0)
794 {
795 throw new NoSuchMethodException();
796 }
797 if (methods.length == 1)
798 {
799 return methods[0];
800 }
801
802 int resultID = 0;
803 while (resultID < methods.length)
804 {
805
806 boolean success = true;
807 for (int i = 0; i < methods.length; i++)
808 {
809 if (resultID == i)
810 {
811 continue;
812 }
813 if (!isMoreSpecific(methods[resultID], methods[i]))
814 {
815 success = false;
816 }
817 }
818
819 if (success)
820 {
821 return methods[resultID];
822 }
823 resultID++;
824 }
825
826 throw new NoSuchMethodException();
827 }
828
829
830
831
832
833
834
835
836
837 private static Method getSpecificMethod(final Method[] methods) throws NoSuchMethodException
838 {
839
840 if (methods.length == 0)
841 {
842 throw new NoSuchMethodException();
843 }
844 if (methods.length == 1)
845 {
846 return methods[0];
847 }
848
849 int resultID = 0;
850 while (resultID < methods.length)
851 {
852
853 boolean success = true;
854 for (int i = 0; i < methods.length; i++)
855 {
856 if (resultID == i)
857 {
858 continue;
859 }
860 if (!isMoreSpecific(methods[resultID], methods[i]))
861 {
862 success = false;
863 }
864 }
865
866 if (success)
867 {
868 return methods[resultID];
869 }
870 resultID++;
871 }
872
873 throw new NoSuchMethodException();
874 }
875
876
877
878
879
880
881
882
883
884 private static Method resolveMethodSuper(final Class<?> clazz, final String name, final Class<?>[] parameterTypes)
885 throws NoSuchMethodException
886 {
887 String key = "METHOD:" + clazz + "@" + name + "@" + FieldSignature.toDescriptor(parameterTypes);
888 try
889 {
890 if (CACHE.containsKey(key))
891 {
892 return (Method) CACHE.get(key);
893 }
894 Method method = clazz.getDeclaredMethod(name, parameterTypes);
895 CACHE.put(key, method);
896 return method;
897 }
898 catch (Exception exception)
899 {
900 if (clazz.getSuperclass() != null)
901 {
902 Method method = ClassUtil.resolveMethodSuper(clazz.getSuperclass(), name, parameterTypes);
903 CACHE.put(key, method);
904 return method;
905 }
906 throw new NoSuchMethodException(exception.getMessage());
907 }
908 }
909
910
911
912
913
914
915
916
917 private static Field resolveFieldSuper(final Class<?> clazz, final String fieldName) throws NoSuchFieldException
918 {
919 String key = "FIELD:" + clazz + "@" + fieldName;
920 try
921 {
922 if (CACHE.containsKey(key))
923 {
924 return (Field) CACHE.get(key);
925 }
926 Field result = clazz.getDeclaredField(fieldName);
927 CACHE.put(key, result);
928 return result;
929 }
930 catch (Exception exception)
931 {
932 if (clazz.getSuperclass() != null)
933 {
934 Field result = ClassUtil.resolveFieldSuper(clazz.getSuperclass(), fieldName);
935 CACHE.put(key, result);
936 return result;
937 }
938 throw new NoSuchFieldException(exception.getMessage());
939 }
940 }
941
942
943
944
945
946
947
948
949 private static Annotation resolveAnnotationSuper(final Class<?> clazz, final Class<? extends Annotation> annotationClass)
950 throws NoSuchElementException
951 {
952 String key = "ANNOTATION:" + clazz + "@" + annotationClass;
953 try
954 {
955 if (CACHE.containsKey(key))
956 {
957 return (Annotation) CACHE.get(key);
958 }
959 Annotation[] annotations = clazz.getDeclaredAnnotations();
960 Annotation result = null;
961 for (Annotation annotation : annotations)
962 {
963 if (annotation.annotationType().equals(annotationClass))
964 {
965 result = annotation;
966 break;
967 }
968 }
969 if (result == null)
970 {
971 throw new NoSuchElementException("Annotation " + annotationClass + " not found in class " + clazz.getName());
972 }
973 CACHE.put(key, result);
974 return result;
975 }
976 catch (Exception exception)
977 {
978 if (clazz.getSuperclass() != null)
979 {
980 Annotation result = ClassUtil.resolveAnnotationSuper(clazz.getSuperclass(), annotationClass);
981 CACHE.put(key, result);
982 return result;
983 }
984 throw new NoSuchElementException(exception.getMessage());
985 }
986 }
987
988
989
990
991
992
993
994
995
996
997
998 @SuppressWarnings("unchecked")
999 public static void changeAnnotationValue(final Annotation annotation, final String key, final Object newValue)
1000 {
1001 Object handler = Proxy.getInvocationHandler(annotation);
1002 Field f;
1003 try
1004 {
1005 f = handler.getClass().getDeclaredField("memberValues");
1006 }
1007 catch (NoSuchFieldException | SecurityException e)
1008 {
1009 throw new IllegalStateException(e);
1010 }
1011 f.setAccessible(true);
1012 Map<String, Object> memberValues;
1013 try
1014 {
1015 memberValues = (Map<String, Object>) f.get(handler);
1016 }
1017 catch (IllegalArgumentException | IllegalAccessException e)
1018 {
1019 throw new IllegalStateException(e);
1020 }
1021 Object oldValue = memberValues.get(key);
1022 if (oldValue == null || oldValue.getClass() != newValue.getClass())
1023 {
1024 throw new IllegalArgumentException();
1025 }
1026 memberValues.put(key, newValue);
1027 }
1028
1029
1030
1031
1032
1033
1034 public static ClassFileDescriptor classFileDescriptor(final Object object)
1035 {
1036 return classFileDescriptor(object.getClass());
1037 }
1038
1039
1040
1041
1042
1043
1044 public static ClassFileDescriptor classFileDescriptor(final Class<?> clazz)
1045 {
1046 URL clazzUrl = URLResource.getResource("/" + clazz.getName().replaceAll("\\.", "/") + ".class");
1047 return classFileDescriptor(clazzUrl);
1048 }
1049
1050
1051
1052
1053
1054
1055 public static ClassFileDescriptor classFileDescriptor(final URL clazzUrl)
1056 {
1057 if (clazzUrl.toString().startsWith("jar:file:") && clazzUrl.toString().contains("!"))
1058 {
1059 String[] parts = clazzUrl.toString().split("\\!");
1060 String jarFileName = parts[0].replace("jar:file:", "");
1061 try
1062 {
1063 URL jarURL = new URL("file:" + jarFileName);
1064 File jarUrlFile = new File(jarURL.toURI());
1065 try (JarFile jarFile = new JarFile(jarUrlFile))
1066 {
1067 if (parts[1].startsWith("/"))
1068 {
1069 parts[1] = parts[1].substring(1);
1070 }
1071 JarEntry jarEntry = jarFile.getJarEntry(parts[1]);
1072 return new ClassFileDescriptor(jarEntry, jarFileName + "!" + parts[1]);
1073 }
1074 catch (Exception exception)
1075 {
1076 URL jarURL2 = new URL("file:" + jarFileName);
1077 return new ClassFileDescriptor(new File(jarURL2.toURI()));
1078 }
1079 }
1080 catch (Exception exception)
1081 {
1082 return new ClassFileDescriptor(new File(jarFileName));
1083 }
1084 }
1085 try
1086 {
1087 return new ClassFileDescriptor(new File(clazzUrl.toURI()));
1088 }
1089 catch (URISyntaxException exception)
1090 {
1091 return new ClassFileDescriptor(new File(clazzUrl.getPath()));
1092 }
1093 }
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104 public static class ClassFileDescriptor
1105 {
1106
1107 private final String name;
1108
1109
1110 private final String path;
1111
1112
1113 private final boolean jar;
1114
1115
1116 private long lastChangedDate;
1117
1118
1119
1120
1121
1122 public ClassFileDescriptor(final File classFile)
1123 {
1124 this.name = classFile.getName();
1125 this.path = classFile.getPath();
1126 this.jar = false;
1127 long lastModified = classFile.lastModified();
1128 if (lastModified == 0L)
1129 {
1130 BasicFileAttributes attributes;
1131 try
1132 {
1133 attributes = Files.readAttributes(Paths.get(this.path), BasicFileAttributes.class);
1134 lastModified = attributes.lastModifiedTime().toMillis();
1135 }
1136 catch (IOException exception)
1137 {
1138
1139 }
1140 }
1141 this.lastChangedDate = lastModified;
1142 }
1143
1144
1145
1146
1147
1148
1149 public ClassFileDescriptor(final JarEntry jarEntry, final String path)
1150 {
1151 this.name = jarEntry.getName();
1152 this.path = path;
1153 this.jar = true;
1154 this.lastChangedDate = jarEntry.getLastModifiedTime().toMillis();
1155 }
1156
1157
1158
1159
1160
1161
1162 public ClassFileDescriptor(final ZipEntry zipEntry, final String path)
1163 {
1164 this.name = zipEntry.getName();
1165 this.path = path;
1166 this.jar = true;
1167 this.lastChangedDate = zipEntry.getLastModifiedTime().toMillis();
1168 }
1169
1170
1171
1172
1173 public String getName()
1174 {
1175 return this.name;
1176 }
1177
1178
1179
1180
1181 public String getPath()
1182 {
1183 return this.path;
1184 }
1185
1186
1187
1188
1189 public boolean isJar()
1190 {
1191 return this.jar;
1192 }
1193
1194
1195
1196
1197 public long getLastChangedDate()
1198 {
1199 return this.lastChangedDate;
1200 }
1201
1202
1203 @Override
1204 public String toString()
1205 {
1206 return "ClassFileDescriptor [name=" + this.name + ", path=" + this.path + ", jar=" + this.jar + ", lastChangedDate="
1207 + this.lastChangedDate + "]";
1208 }
1209 }
1210 }