1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sourceforge.domian.util;
17
18
19 import java.io.Serializable;
20 import java.lang.reflect.AccessibleObject;
21 import java.lang.reflect.Array;
22 import java.lang.reflect.Constructor;
23 import java.lang.reflect.Field;
24 import java.lang.reflect.GenericArrayType;
25 import java.lang.reflect.InvocationTargetException;
26 import java.lang.reflect.Method;
27 import java.lang.reflect.Modifier;
28 import java.lang.reflect.ParameterizedType;
29 import java.lang.reflect.Type;
30 import java.lang.reflect.TypeVariable;
31 import java.math.BigDecimal;
32 import java.security.PrivilegedAction;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.Date;
36 import java.util.HashMap;
37 import java.util.Iterator;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.UUID;
41
42 import org.apache.commons.lang.Validate;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 import net.sourceforge.domian.entity.AbstractEntity;
47 import net.sourceforge.domian.entity.Entity;
48
49 import static java.lang.Boolean.TRUE;
50 import static java.security.AccessController.doPrivileged;
51 import static org.apache.commons.lang.StringUtils.capitalize;
52
53
54
55
56
57
58
59
60 public final class ReflectionUtils {
61
62 private static final Logger log = LoggerFactory.getLogger(ReflectionUtils.class);
63
64
65 public static Boolean canCast(final Class fromClass, final Class toClass) {
66 return canCastFrom_To(fromClass, toClass);
67 }
68
69
70 public static Boolean canCast(final Type fromType, final Type toType) {
71 return canCastFrom_To(fromType, toType);
72 }
73
74
75 public static Boolean canCastFrom_To(final Class fromClass, final Class toClass) {
76 if (fromClass == null) {
77 return toClass == null;
78 } else if (toClass == null) {
79 return false;
80 }
81 if (fromClass.isPrimitive()) {
82 if (fromClass.equals(boolean.class)) {
83 return canCastFrom_To(Boolean.class, toClass);
84
85 } else if (fromClass.equals(long.class)) {
86 return canCastFrom_To(Long.class, toClass);
87
88 } else if (fromClass.equals(int.class)) {
89 return canCastFrom_To(Integer.class, toClass);
90
91 } else if (fromClass.equals(double.class)) {
92 return canCastFrom_To(Double.class, toClass);
93
94 } else if (fromClass.equals(float.class)) {
95 return canCastFrom_To(Float.class, toClass);
96
97 } else if (fromClass.equals(short.class)) {
98 return canCastFrom_To(Short.class, toClass);
99 }
100 }
101 if (Serializable.class.isAssignableFrom(fromClass) && Serializable.class.isAssignableFrom(toClass)) {
102 if (CharSequence.class.isAssignableFrom(fromClass)) {
103 if (Number.class.isAssignableFrom(toClass)) {
104 return TRUE;
105 }
106 }
107 if (CharSequence.class.isAssignableFrom(toClass)) {
108 if (Number.class.isAssignableFrom(fromClass)) {
109 return TRUE;
110 }
111 }
112 }
113 return fromClass.equals(toClass) || toClass.isAssignableFrom(fromClass);
114 }
115
116
117
118 public static Boolean canCastFrom_To(final Type fromType, final Type toType) {
119 if (fromType instanceof Class && toType instanceof Class) {
120 return canCastFrom_To((Class) fromType, (Class) toType);
121 }
122 if (fromType == null) {
123 return toType == null;
124 } else if (toType == null) {
125 return false;
126 }
127 return fromType.equals(toType);
128 }
129
130
131
132 public static Boolean canCastAtLeastOneWay(final Type type1, final Type type2) {
133 return canCastFrom_To(type1, type2) || canCastFrom_To(type2, type1);
134 }
135
136
137
138 public static Boolean isDecimalNumber(final Object number) {
139 return canCast(number.getClass(), Double.class) ||
140 canCast(number.getClass(), Float.class) ||
141 canCast(number.getClass(), BigDecimal.class);
142 }
143
144
145 public static Boolean isDate(final Object candidateObject) {
146 return canCast(candidateObject.getClass(), Date.class);
147 }
148
149
150
151 public static <T> Field getFieldByName(final String fieldName, final Class<T> declaringType) {
152 Validate.notNull(fieldName, "Field name parameter cannot be null");
153 Validate.notNull(declaringType, "Type parameter cannot be null");
154
155 Field field = null;
156 try {
157 field = declaringType.getDeclaredField(fieldName);
158 } catch (NoSuchFieldException e) {
159
160 }
161 if (field == null) {
162 final Class<? super T> superType = declaringType.getSuperclass();
163 if (superType != null) {
164 field = getFieldByName(fieldName, superType);
165 }
166 }
167 return field;
168 }
169
170
171
172
173
174
175 public static <T> Method getMethodByName(final String methodName, final Class<T> declaringType) {
176 return getMethodByName(methodName, null, declaringType);
177 }
178
179
180
181
182
183
184 public static <T> Method getMethodByName(final String methodName, final Class[] parameterArray, final Class<T> declaringType) {
185 Validate.notNull(methodName, "Method name parameter cannot be null");
186 Validate.notNull(declaringType, "Type parameter cannot be null");
187
188 Method method = null;
189 try {
190 method = declaringType.getDeclaredMethod(methodName, parameterArray);
191 } catch (NoSuchMethodException e) {
192
193 }
194 if (method == null) {
195 final Class<? super T> superType = declaringType.getSuperclass();
196 if (superType != null) {
197 method = getMethodByName(methodName, parameterArray, superType);
198 }
199 }
200 return method;
201 }
202
203
204
205
206
207
208
209
210 public static <T> Method getMethodByNameWithPossiblePrefix(final String methodName,
211 final Class<T> declaringType,
212 final List<String> possibleMethodPrefixes) {
213 Method method = getMethodByName(methodName, declaringType);
214 final Iterator<String> possibleMethodPrefixesIterator = possibleMethodPrefixes.iterator();
215 while (method == null && possibleMethodPrefixesIterator.hasNext()) {
216 method = getMethodByName(possibleMethodPrefixesIterator.next() + capitalize(methodName), declaringType);
217 }
218 return method;
219 }
220
221
222
223 public static <T> Object invokeMethod(final T object, final String methodName, final Object[] parameterArray) {
224 final Method method;
225 if (parameterArray == null || parameterArray.length < 1) {
226 method = getMethodByName(methodName, null, object.getClass());
227 } else {
228 final Class[] parameterTypeArray = new Class[parameterArray.length];
229 final List<Class<?>> parameterTypeList = new ArrayList<Class<?>>();
230 for (final Object parameter : parameterArray) {
231 parameterTypeList.add(parameter.getClass());
232 }
233 method = getMethodByName(methodName, parameterTypeList.toArray(parameterTypeArray), object.getClass());
234 }
235 if (method != null) {
236 try {
237 return method.invoke(object, parameterArray);
238
239 } catch (IllegalArgumentException e) {
240 return null;
241 } catch (IllegalAccessException e) {
242 return null;
243 } catch (InvocationTargetException e) {
244 return null;
245 }
246 } else {
247 return null;
248 }
249 }
250
251
252
253 static <T> Object invokeMethod(final T object, final String methodName) {
254 return invokeMethod(object, methodName, null);
255 }
256
257
258
259 static <T> Boolean invokeBooleanMethod(final T object, final String methodName, final Object[] parameterArray) {
260 final Object retVal = invokeMethod(object, methodName, parameterArray);
261 return retVal != null && retVal instanceof Boolean && (Boolean) retVal;
262 }
263
264
265
266 static <T> Boolean invokeBooleanMethod(final T object, final String methodName) {
267 return invokeBooleanMethod(object, methodName, null);
268 }
269
270
271
272
273
274
275 public static Map<String, AccessibleObject> getAllAccessibleObjectsFrom(final Object object) {
276 final Map<String, AccessibleObject> accessibleObjectMap = new HashMap<String, AccessibleObject>();
277 final Method[] methods = object.getClass().getDeclaredMethods();
278 for (final Method method : methods) {
279 accessibleObjectMap.put(method.getName(), method);
280 }
281 final Field[] fields = object.getClass().getDeclaredFields();
282 for (final Field field : fields) {
283 accessibleObjectMap.put(field.getName(), field);
284 }
285 return accessibleObjectMap;
286 }
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308 public static Boolean DO_COPY_OBJECTS = true;
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329 public static Boolean DO_COPY_ENTITIES = false;
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346 public static int RECURSIVE_COPYING_DEPTH_TRESHOLD = 5;
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361 public static <T> T replicate(final T object) {
362 return cloneOrDeepCopyIfNotImmutable(object);
363 }
364
365
366
367
368
369
370
371
372 public static <T> T cloneOrDeepCopyIfNotImmutable(final T object) {
373 return cloneOrDeepCopyIfNotImmutable(object, DO_COPY_ENTITIES);
374 }
375
376
377
378
379
380
381
382
383
384 public static <T> T cloneOrDeepCopyIfNotImmutable(final T object, final Boolean doCopyEntities) {
385 return cloneOrDeepCopyIfNotImmutable(object, new HashMap(), 0, RECURSIVE_COPYING_DEPTH_TRESHOLD, DO_COPY_OBJECTS, doCopyEntities, false);
386 }
387
388
389
390
391
392
393
394 @SuppressWarnings("unchecked")
395 static <T> T cloneOrDeepCopyIfNotImmutable(final T object,
396 final Map mapOfObjectsInprocess,
397 int recursiveDepth,
398 final int recursiveDepthTreshold,
399 final boolean doCopyObject,
400 final boolean doCopyEntities,
401 final boolean entityCopyingInProcess) {
402 if (!doCopyObject) {
403 return object;
404 }
405 if (recursiveDepth > recursiveDepthTreshold) {
406 return null;
407 }
408 if (object == null) {
409 return null;
410 }
411 if (isAlreadyBeingProcessed(object, mapOfObjectsInprocess)) {
412
413 return (T) mapOfObjectsInprocess.get(object);
414 }
415 if (isImmutableObject(object)) {
416 return object;
417 }
418 if (isEntity(object) && !entityCopyingInProcess) {
419 if (!doCopyEntities) {
420 return object;
421 } else {
422 T entityCopy = (T) copyEntity((Entity) object, mapOfObjectsInprocess, recursiveDepth, recursiveDepthTreshold, doCopyObject);
423 if (entityCopy != null) {
424 return entityCopy;
425 } else {
426 log.warn("Unable to deep copy entity object=" + object + "; trying ordinary deep copying...");
427 }
428 }
429 }
430 if (isCloneableObject(object)) {
431 final T clonedObject = doCloneIfNotTheCloningIsJustShallowCopying(object);
432 if (clonedObject != null) {
433 return clonedObject;
434 }
435 }
436
437
438 final T copiedObject;
439
440
441 if (object instanceof Collection) {
442 try {
443
444 final Constructor<T> defaultConstructor = (Constructor<T>) object.getClass().getDeclaredConstructor();
445 doPrivileged(new PrivilegedAction<Void>() {
446 public Void run() {
447 defaultConstructor.setAccessible(true);
448 return null;
449 }
450 });
451 copiedObject = defaultConstructor.newInstance();
452
453 } catch (NoSuchMethodException e) {
454
455 log.warn("Unable to deep copy object=" + object + " due to missing default constructor; just returning object itself... beware of state changes if object is mutable!");
456
457 return object;
458
459 } catch (IllegalAccessException e) {
460
461 e.printStackTrace();
462 return object;
463
464 } catch (InvocationTargetException e) {
465
466 e.printStackTrace();
467 return object;
468
469 } catch (InstantiationException e) {
470
471 e.printStackTrace();
472 return object;
473
474 } catch (Throwable t) {
475
476 log.warn("Unable to deep copy object=" + object + ". Exception caught: " + t.getMessage() + "; just returning object itself... beware of state changes if object is mutable!");
477 t.printStackTrace();
478 return object;
479 }
480 ++recursiveDepth;
481 for (final Object element : (Collection) object) {
482
483 ((Collection) copiedObject).add(
484 cloneOrDeepCopyIfNotImmutable(element,
485 mapOfObjectsInprocess,
486 recursiveDepth,
487 recursiveDepthTreshold,
488 doCopyObject,
489 doCopyEntities,
490 false));
491 }
492 return copiedObject;
493 }
494
495 if (object instanceof Map) {
496 try {
497
498 final Constructor<T> defaultConstructor = (Constructor<T>) object.getClass().getDeclaredConstructor();
499 doPrivileged(new PrivilegedAction<Void>() {
500 public Void run() {
501 defaultConstructor.setAccessible(true);
502 return null;
503 }
504 });
505 copiedObject = defaultConstructor.newInstance();
506
507 } catch (NoSuchMethodException e) {
508
509 log.warn("Unable to deep copy object=" + object + " due to missing default constructor; just returning object itself... beware of state changes if object is mutable!");
510
511 return object;
512
513 } catch (IllegalAccessException e) {
514
515 e.printStackTrace();
516 return object;
517
518 } catch (InvocationTargetException e) {
519
520 e.printStackTrace();
521 return object;
522
523 } catch (InstantiationException e) {
524
525 e.printStackTrace();
526 return object;
527
528 } catch (Throwable t) {
529
530 log.warn("Unable to deep copy object=" + object + ". Exception caught: " + t.getMessage() + "; just returning object itself... beware of state changes if object is mutable!");
531 t.printStackTrace();
532 return object;
533 }
534 ++recursiveDepth;
535 for (final Object valueKey : ((Map) object).keySet()) {
536 final Object value = ((Map) object).get(valueKey);
537
538 ((Map) copiedObject).put(cloneOrDeepCopyIfNotImmutable(valueKey,
539 mapOfObjectsInprocess,
540 recursiveDepth,
541 recursiveDepthTreshold,
542 doCopyObject,
543 doCopyEntities,
544 false),
545 cloneOrDeepCopyIfNotImmutable(value,
546 mapOfObjectsInprocess,
547 recursiveDepth,
548 recursiveDepthTreshold,
549 doCopyObject,
550 doCopyEntities,
551 false));
552 }
553 return copiedObject;
554
555 }
556
557 if (object.getClass().isArray()) {
558 final int arrayLength = Array.getLength(object);
559
560 copiedObject = (T) Array.newInstance(object.getClass().getComponentType(), arrayLength);
561 ++recursiveDepth;
562 for (int arrayElementIndex = 0; arrayElementIndex < arrayLength; ++arrayElementIndex) {
563 final Object arrayElement = Array.get(object, arrayElementIndex);
564 if (arrayElement != null) {
565 Array.set(copiedObject,
566 arrayElementIndex,
567 cloneOrDeepCopyIfNotImmutable(arrayElement,
568 mapOfObjectsInprocess,
569 recursiveDepth,
570 recursiveDepthTreshold,
571 doCopyObject,
572 doCopyEntities,
573 false));
574 }
575 }
576 return copiedObject;
577 }
578
579 try {
580
581 final Constructor<T> defaultConstructor = (Constructor<T>) object.getClass().getDeclaredConstructor();
582 doPrivileged(new PrivilegedAction<Void>() {
583 public Void run() {
584 defaultConstructor.setAccessible(true);
585 return null;
586 }
587 });
588 copiedObject = defaultConstructor.newInstance();
589
590
591 mapOfObjectsInprocess.put(object, copiedObject);
592 ++recursiveDepth;
593 for (final Field field : object.getClass().getDeclaredFields()) {
594 if (!Modifier.isStatic(field.getModifiers())) {
595 doPrivileged(new PrivilegedAction<Void>() {
596 public Void run() {
597 field.setAccessible(true);
598 return null;
599 }
600 });
601 final Object clonedField =
602 cloneOrDeepCopyIfNotImmutable(field.get(object),
603 mapOfObjectsInprocess,
604 recursiveDepth,
605 recursiveDepthTreshold,
606 doCopyObject,
607 doCopyEntities,
608 false);
609 field.set(copiedObject, clonedField);
610 }
611 }
612 } catch (NoSuchMethodException e) {
613
614 log.warn("Unable to deep copy object=" + object + " due to missing default constructor; just returning object itself... beware of state changes if object is mutable!");
615
616 return object;
617
618 } catch (IllegalAccessException e) {
619
620 e.printStackTrace();
621 return object;
622
623 } catch (InvocationTargetException e) {
624
625 e.printStackTrace();
626 return object;
627
628 } catch (InstantiationException e) {
629
630 e.printStackTrace();
631 return object;
632
633 } catch (Throwable t) {
634
635 log.warn("Unable to deep copy object=" + object + ". Exception caught: " + t.getMessage());
636 t.printStackTrace();
637 return object;
638 }
639 return copiedObject;
640 }
641
642
643
644 static <T> Boolean isAlreadyBeingProcessed(final T object, final Map<?, ?> mapOfObjectsBeingProcessed) {
645 return mapOfObjectsBeingProcessed.keySet().contains(object);
646 }
647
648
649
650 static <T> Boolean isCloneableObject(final T object) {
651 return canCastFrom_To(object.getClass(), Cloneable.class);
652 }
653
654
655
656 static <T> Boolean isImmutableObject(final T object) {
657 if (object.getClass().isArray()) {
658 return false;
659 }
660 if (object instanceof String ||
661 object instanceof Boolean ||
662 object instanceof Number ||
663 object instanceof Enum ||
664 object instanceof UUID) {
665 return true;
666 }
667 if (object.getClass().getDeclaredFields().length == 0) {
668 return true;
669 }
670 if (invokeBooleanMethod(object, "isImmutable")) {
671 return true;
672 }
673 if (invokeBooleanMethod(object, "isValueObject")) {
674 return true;
675 }
676
677
678
679
680
681
682
683
684 return false;
685 }
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701 public static <T> Boolean isEntity(final T object) {
702 return object instanceof AbstractEntity;
703 }
704
705
706
707 static boolean hasEntityIdField(final Object object) {
708 return getEntityIdField(object) != null;
709 }
710
711
712
713 static <T> Field getEntityIdField(final T object) {
714 final Field idField = getFieldByName("entityId", object.getClass());
715
716
717
718
719
720
721
722
723
724
725
726 return idField;
727 }
728
729
730
731
732
733
734
735
736 static <T extends Entity> T copyEntity(final T entityObject,
737 final Map<T, T> mapOfObjectsBeingProcessed,
738 int recursiveDepth,
739 final int recursiveDepthTreshold,
740 final boolean doCopyObject) {
741 final Field entityIdField = getEntityIdField(entityObject);
742 if (entityIdField == null) {
743 throw new IllegalArgumentException("Unable to copy entity of type=" + entityObject.getClass().getName() + ". The class seems not to posess a proper entityId field.");
744 }
745
746 try {
747 final T copiedEntityObject = cloneOrDeepCopyIfNotImmutable(entityObject,
748 mapOfObjectsBeingProcessed,
749 recursiveDepth,
750 recursiveDepthTreshold,
751 doCopyObject,
752 true,
753 true);
754
755 mapOfObjectsBeingProcessed.put(entityObject, copiedEntityObject);
756
757 doPrivileged(new PrivilegedAction<Void>() {
758 public Void run() {
759 entityIdField.setAccessible(true);
760 return null;
761 }
762 });
763 final Object entityId = entityIdField.get(entityObject);
764 entityIdField.set(copiedEntityObject, entityId);
765
766 return copiedEntityObject;
767
768 } catch (IllegalAccessException e) {
769 throw new IllegalArgumentException("Unable to copy entity object " + entityObject.getClass().getName(), e);
770 }
771 }
772
773
774
775
776
777
778 @SuppressWarnings("unchecked")
779 static <T> T doCloneIfNotTheCloningIsJustShallowCopying(final T object) {
780 if (object instanceof Collection) {
781 return null;
782
783 } else if (object instanceof Map) {
784 return null;
785
786 } else if (object.getClass().isArray()) {
787 return null;
788
789 } else {
790
791 return (T) invokeMethod(object, "clone");
792 }
793 }
794
795
796
797
798
799
800
801
802 public static Class<?> getClass(final Type type) {
803 if (type instanceof Class) {
804 return (Class) type;
805
806 } else if (type instanceof ParameterizedType) {
807 return getClass(((ParameterizedType) type).getRawType());
808
809 } else if (type instanceof GenericArrayType) {
810 final Type componentType = ((GenericArrayType) type).getGenericComponentType();
811 final Class<?> componentClass = getClass(componentType);
812 if (componentClass != null) {
813 return Array.newInstance(componentClass, 0).getClass();
814 } else {
815 return null;
816 }
817
818 } else {
819 return null;
820 }
821 }
822
823
824
825
826
827
828
829
830
831
832
833 public static <T> List<Class<?>> getTypeArguments(final Class<T> baseClass, final Class<? extends T> subclass) {
834 final Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
835 Type type = subclass;
836
837 while (!getClass(type).equals(baseClass)) {
838 if (type instanceof Class) {
839
840 type = ((Class) type).getGenericSuperclass();
841
842 } else {
843 final ParameterizedType parameterizedType = (ParameterizedType) type;
844 final Class<?> rawType = (Class) parameterizedType.getRawType();
845
846 final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
847 final TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
848 for (int i = 0; i < actualTypeArguments.length; ++i) {
849 resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
850 }
851
852 if (!rawType.equals(baseClass)) {
853 type = rawType.getGenericSuperclass();
854 }
855 }
856 }
857
858 final Type[] actualTypeArguments;
859 if (type instanceof Class) {
860 actualTypeArguments = ((Class) type).getTypeParameters();
861 } else {
862 actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
863 }
864
865 final List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
866
867 for (Type baseType : actualTypeArguments) {
868 while (resolvedTypes.containsKey(baseType)) {
869 baseType = resolvedTypes.get(baseType);
870 }
871 typeArgumentsAsClasses.add(getClass(baseType));
872 }
873
874 return typeArgumentsAsClasses;
875 }
876 }