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
109
110 Class<?> c = original.getClass();
111 Field[] fields = getFields(c, false);
112 try {
113 Object copy = instantiate(original);
114
115
116 cache.put(original, copy);
117
118
119 for (Field f : fields) {
120 Object object = f.get(original);
121
122 boolean referenceCopy = false;
123 boolean referenceCollectionCopy = false;
124 ReferenceCopy copyAnnotation = f.getAnnotation(ReferenceCopy.class);
125 if (copyAnnotation != null) {
126 referenceCopy = true;
127 referenceCollectionCopy = copyAnnotation.newCollectionInstance();
128 }
129
130 if (!referenceCopy || referenceCollectionCopy) {
131 object = CloneUtils.deepCloneReflectionInternal(object, cache, referenceCollectionCopy);
132 }
133 f.set(copy, object);
134 }
135
136 return copy;
137 }
138 catch (Throwable t) {
139 LOG.debug("Exception during clone (returning original): " + t.getMessage());
140 return original;
141 }
142 }
143
144 @SuppressWarnings("unchecked")
145 protected static Object deepCloneMap(Object original, Map<Object, Object> cache, boolean referenceCollectionCopy)
146 throws Exception {
147
148 Map<Object, Object> clone = (Map<Object, Object>) instantiate(original);
149
150
151 for (Entry<Object, Object> entry : ((Map<Object, Object>) original).entrySet()) {
152 if (referenceCollectionCopy) {
153 clone.put(entry.getKey(), entry.getValue());
154 }
155 else {
156 clone.put(deepCloneReflectionInternal(entry.getKey(), cache, false),
157 deepCloneReflectionInternal(entry.getValue(), cache, false));
158 }
159 }
160
161 return clone;
162 }
163
164 @SuppressWarnings("unchecked")
165 protected static Object deepCloneList(Object original, Map<Object, Object> cache, boolean referenceCollectionCopy)
166 throws Exception {
167
168 List<Object> clone = (List<Object>) instantiate(original);
169
170
171 for (Iterator<Object> iterator = ((List<Object>) original).iterator(); iterator.hasNext();) {
172 Object object = iterator.next();
173 if (referenceCollectionCopy) {
174 clone.add(object);
175 }
176 else {
177 clone.add(deepCloneReflectionInternal(object, cache, false));
178 }
179 }
180
181 return clone;
182 }
183
184
185
186
187
188
189
190
191
192 public static Map<String, Annotation> getFieldsWithAnnotation(Class<?> clazz,
193 Class<? extends Annotation> annotationClass) {
194 Map<String, Annotation> annotationFields = new HashMap<String, Annotation>();
195
196 Field[] fields = getFields(clazz, false);
197 for (Field f : fields) {
198 Annotation fieldAnnotation = f.getAnnotation(annotationClass);
199 if (fieldAnnotation != null) {
200 annotationFields.put(f.getName(), fieldAnnotation);
201 }
202 }
203
204 return annotationFields;
205 }
206
207
208
209
210
211
212
213
214
215 public static boolean fieldHasAnnotation(Class<?> clazz, String propertyName,
216 Class<? extends Annotation> annotationClass) {
217 Field[] fields = getFields(clazz, false);
218 for (int i = 0; i < fields.length; i++) {
219 Field field = fields[i];
220 if (field.getName().equals(propertyName)) {
221 Annotation fieldAnnotation = field.getAnnotation(annotationClass);
222 if (fieldAnnotation != null) {
223 return true;
224 } else {
225 return false;
226 }
227 }
228 }
229
230 return false;
231 }
232
233 protected static final Object instantiate(Object original) throws InstantiationException, IllegalAccessException {
234 return original.getClass().newInstance();
235 }
236
237 public static Field[] getFields(Object object, boolean includeStatic) {
238 return getFields(object, includeStatic, true);
239 }
240
241 public static Field[] getFields(Object object, boolean includeStatic, boolean includeTransient) {
242 Class<?> c = object.getClass();
243 return getFields(c, includeStatic, includeTransient);
244 }
245
246 public static Field[] getFields(Class<?> c, boolean includeStatic) {
247 return getFields(c, includeStatic, true);
248 }
249
250 public static Field[] getFields(Class<?> c, boolean includeStatic, boolean includeTransient) {
251 String cacheKey = c.getCanonicalName() + ":" + includeStatic;
252 Field[] array = fieldCache.get(cacheKey);
253
254 if (array == null) {
255 ArrayList<Field> fields = new ArrayList<Field>();
256
257 List<Class<?>> classes = getClassHierarchy(c, false);
258
259
260 Collections.reverse(classes);
261
262 for (Class<?> clazz : classes) {
263 Field[] allFields = clazz.getDeclaredFields();
264 for (Field f : allFields) {
265 if ((!includeTransient) && ((f.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT)) {
266 continue;
267 }
268 else if (f.isSynthetic()) {
269
270 continue;
271 }
272 boolean isStatic = (f.getModifiers() & Modifier.STATIC) == Modifier.STATIC;
273 if ((isStatic) && (!includeStatic)) {
274 continue;
275 }
276 if (f.getName().equalsIgnoreCase("serialVersionUID")) {
277 continue;
278 }
279 f.setAccessible(true);
280 fields.add(f);
281 }
282 }
283
284 array = fields.toArray(new Field[fields.size()]);
285 fieldCache.put(cacheKey, array);
286 }
287 return array;
288 }
289
290 protected static final Field internalField(Object object, String fieldName) {
291 if (object == null) {
292 System.out.println("Internal Field: " + object + ", " + fieldName);
293 return null;
294 }
295
296 String key = object.getClass().getCanonicalName() + "." + fieldName;
297 Field field = internalFields.get(key);
298 if (field == null) {
299 Field[] fields = getFields(object.getClass(), false);
300
301 for (Field f : fields) {
302 String name = f.getName();
303 if (name.equals(fieldName)) {
304 field = f;
305 internalFields.put(key, field);
306 break;
307 }
308 }
309 }
310
311 return field;
312 }
313
314 protected static List<Class<?>> getClassHierarchy(Class<?> c, boolean includeInterfaces) {
315 List<Class<?>> classes = new ArrayList<Class<?>>();
316 while (c != Object.class) {
317 classes.add(c);
318 if (includeInterfaces) {
319 Class<?>[] interfaces = c.getInterfaces();
320 for (Class<?> i : interfaces) {
321 classes.add(i);
322 }
323 }
324 c = c.getSuperclass();
325 if (c == null) {
326 break;
327 }
328 }
329
330 return classes;
331 }
332
333 }