1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.uif.util;
17
18 import org.kuali.rice.core.api.exception.RiceRuntimeException;
19 import org.kuali.rice.krad.uif.component.ReferenceCopy;
20
21 import java.lang.annotation.Annotation;
22 import java.lang.reflect.Field;
23 import java.lang.reflect.Modifier;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.concurrent.atomic.AtomicInteger;
32 import java.util.concurrent.atomic.AtomicLong;
33
34
35
36
37
38
39
40
41 public class CloneUtils {
42 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CloneUtils.class);
43
44 private static final Map<String, Field[]> fieldCache = new HashMap<String, Field[]>();
45 private static final Map<String, Field> internalFields = new HashMap<String, Field>();
46
47 public static final <O> O deepClone(O original) {
48 try {
49 return deepCloneReflection(original);
50 } catch (Exception e) {
51 throw new RiceRuntimeException(e);
52 }
53 }
54
55 @SuppressWarnings("unchecked")
56 public static final <O> O deepCloneReflection(O original) throws Exception {
57 return (O) deepCloneReflectionInternal(original, new HashMap<Object, Object>(), false);
58 }
59
60 protected static final Object deepCloneReflectionInternal(Object original, Map<Object, Object> cache,
61 boolean referenceCollectionCopy) throws Exception {
62 if (original == null) {
63 return original;
64 }
65 else if (cache.containsKey(original)) {
66 return cache.get(original);
67 }
68
69
70 Object clone = null;
71 if (List.class.isAssignableFrom(original.getClass())) {
72 clone = deepCloneList(original, cache, referenceCollectionCopy);
73 }
74 else if (Map.class.isAssignableFrom(original.getClass())) {
75 clone = deepCloneMap(original, cache, referenceCollectionCopy);
76 }
77 else {
78 clone = deepCloneObject(original, cache);
79 }
80
81 return clone;
82 }
83
84 protected static Object deepCloneObject(Object original, Map<Object, Object> cache) throws Exception {
85 if (original instanceof Number) {
86 if (original instanceof AtomicInteger) {
87
88 }
89 else if (original instanceof AtomicLong) {
90
91 }
92 else {
93 return original;
94 }
95 }
96 else if (original instanceof String) {
97 return original;
98 }
99 else if (original instanceof Character) {
100 return original;
101 }
102 else if (original instanceof Class) {
103 return original;
104 }
105 else if (original instanceof Boolean) {
106 return new Boolean(((Boolean) original).booleanValue());
107 }
108 else if (original instanceof Enum) {
109 return original;
110 }
111
112
113 Class<?> c = original.getClass();
114 Field[] fields = getFields(c, false);
115 try {
116 Object copy = instantiate(original);
117
118
119 cache.put(original, copy);
120
121
122 for (Field f : fields) {
123 Object object = f.get(original);
124
125 boolean referenceCopy = false;
126 boolean referenceCollectionCopy = false;
127 ReferenceCopy copyAnnotation = f.getAnnotation(ReferenceCopy.class);
128 if (copyAnnotation != null) {
129 referenceCopy = true;
130 referenceCollectionCopy = copyAnnotation.newCollectionInstance();
131 }
132
133 if (!referenceCopy || referenceCollectionCopy) {
134 object = CloneUtils.deepCloneReflectionInternal(object, cache, referenceCollectionCopy);
135 }
136 f.set(copy, object);
137 }
138
139 return copy;
140 }
141 catch (Throwable t) {
142 LOG.warn("Exception during clone (returning original): " + t.getMessage());
143 return original;
144 }
145 }
146
147 @SuppressWarnings("unchecked")
148 protected static Object deepCloneMap(Object original, Map<Object, Object> cache, boolean referenceCollectionCopy)
149 throws Exception {
150
151 Map<Object, Object> clone = (Map<Object, Object>) instantiate(original);
152
153
154 for (Entry<Object, Object> entry : ((Map<Object, Object>) original).entrySet()) {
155 if (referenceCollectionCopy) {
156 clone.put(entry.getKey(), entry.getValue());
157 }
158 else {
159 clone.put(deepCloneReflectionInternal(entry.getKey(), cache, false),
160 deepCloneReflectionInternal(entry.getValue(), cache, false));
161 }
162 }
163
164 return clone;
165 }
166
167 @SuppressWarnings("unchecked")
168 protected static Object deepCloneList(Object original, Map<Object, Object> cache, boolean referenceCollectionCopy)
169 throws Exception {
170
171 List<Object> clone = (List<Object>) instantiate(original);
172
173
174 for (Iterator<Object> iterator = ((List<Object>) original).iterator(); iterator.hasNext();) {
175 Object object = iterator.next();
176 if (referenceCollectionCopy) {
177 clone.add(object);
178 }
179 else {
180 clone.add(deepCloneReflectionInternal(object, cache, false));
181 }
182 }
183
184 return clone;
185 }
186
187
188
189
190
191
192
193
194
195 public static Map<String, Annotation> getFieldsWithAnnotation(Class<?> clazz,
196 Class<? extends Annotation> annotationClass) {
197 Map<String, Annotation> annotationFields = new HashMap<String, Annotation>();
198
199 Field[] fields = getFields(clazz, false);
200 for (Field f : fields) {
201 Annotation fieldAnnotation = f.getAnnotation(annotationClass);
202 if (fieldAnnotation != null) {
203 annotationFields.put(f.getName(), fieldAnnotation);
204 }
205 }
206
207 return annotationFields;
208 }
209
210
211
212
213
214
215
216
217
218 public static boolean fieldHasAnnotation(Class<?> clazz, String propertyName,
219 Class<? extends Annotation> annotationClass) {
220 Field[] fields = getFields(clazz, false);
221 for (int i = 0; i < fields.length; i++) {
222 Field field = fields[i];
223 if (field.getName().equals(propertyName)) {
224 Annotation fieldAnnotation = field.getAnnotation(annotationClass);
225 if (fieldAnnotation != null) {
226 return true;
227 } else {
228 return false;
229 }
230 }
231 }
232
233 return false;
234 }
235
236 protected static final Object instantiate(Object original) throws InstantiationException, IllegalAccessException {
237 return original.getClass().newInstance();
238 }
239
240 public static Field[] getFields(Object object, boolean includeStatic) {
241 return getFields(object, includeStatic, true);
242 }
243
244 public static Field[] getFields(Object object, boolean includeStatic, boolean includeTransient) {
245 Class<?> c = object.getClass();
246 return getFields(c, includeStatic, includeTransient);
247 }
248
249 public static Field[] getFields(Class<?> c, boolean includeStatic) {
250 return getFields(c, includeStatic, true);
251 }
252
253 public static Field[] getFields(Class<?> c, boolean includeStatic, boolean includeTransient) {
254 String cacheKey = c.getCanonicalName() + ":" + includeStatic;
255 Field[] array = fieldCache.get(cacheKey);
256
257 if (array == null) {
258 ArrayList<Field> fields = new ArrayList<Field>();
259
260 List<Class<?>> classes = getClassHierarchy(c, false);
261
262
263 Collections.reverse(classes);
264
265 for (Class<?> clazz : classes) {
266 Field[] allFields = clazz.getDeclaredFields();
267 for (Field f : allFields) {
268 if ((!includeTransient) && ((f.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT)) {
269 continue;
270 }
271 else if (f.isSynthetic()) {
272
273 continue;
274 }
275 boolean isStatic = (f.getModifiers() & Modifier.STATIC) == Modifier.STATIC;
276 if ((isStatic) && (!includeStatic)) {
277 continue;
278 }
279 if (f.getName().equalsIgnoreCase("serialVersionUID")) {
280 continue;
281 }
282 f.setAccessible(true);
283 fields.add(f);
284 }
285 }
286
287 array = fields.toArray(new Field[fields.size()]);
288 fieldCache.put(cacheKey, array);
289 }
290 return array;
291 }
292
293 protected static final Field internalField(Object object, String fieldName) {
294 if (object == null) {
295 System.out.println("Internal Field: " + object + ", " + fieldName);
296 return null;
297 }
298
299 String key = object.getClass().getCanonicalName() + "." + fieldName;
300 Field field = internalFields.get(key);
301 if (field == null) {
302 Field[] fields = getFields(object.getClass(), false);
303
304 for (Field f : fields) {
305 String name = f.getName();
306 if (name.equals(fieldName)) {
307 field = f;
308 internalFields.put(key, field);
309 break;
310 }
311 }
312 }
313
314 return field;
315 }
316
317 protected static List<Class<?>> getClassHierarchy(Class<?> c, boolean includeInterfaces) {
318 List<Class<?>> classes = new ArrayList<Class<?>>();
319 while (c != Object.class) {
320 classes.add(c);
321 if (includeInterfaces) {
322 Class<?>[] interfaces = c.getInterfaces();
323 for (Class<?> i : interfaces) {
324 classes.add(i);
325 }
326 }
327 c = c.getSuperclass();
328 if (c == null) {
329 break;
330 }
331 }
332
333 return classes;
334 }
335
336 }