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