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