1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.sun.syndication.common;
18
19 import java.beans.BeanInfo;
20 import java.beans.Introspector;
21 import java.beans.PropertyDescriptor;
22 import java.io.Serializable;
23 import java.lang.reflect.Array;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Modifier;
26 import java.util.*;
27
28 /***
29 * Provides deep <b>Bean</b> clonning support.
30 * <p>
31 * It works on all read/write properties, recursively. It support all primitive types, Strings, Collections,
32 * Cloneable objects and multi-dimensional arrays of any of them.
33 * <p>
34 * @author Alejandro Abdelnur
35 *
36 */
37 public class CloneableBean implements Serializable, Cloneable {
38
39 private static final Class[] NO_PARAMS_DEF = new Class[0];
40 private static final Object[] NO_PARAMS = new Object[0];
41
42 private Object _obj;
43
44 /***
45 * Default constructor.
46 * <p>
47 * To be used by classes extending CloneableBean only.
48 * <p>
49 *
50 */
51 protected CloneableBean() {
52 _obj = this;
53 }
54
55 /***
56 * Creates a CloneableBean to be used in a delegation pattern.
57 * <p>
58 * For example:
59 * <p>
60 * <code>
61 * public class Foo implements Cloneable {
62 * private CloneableBean _cloneableBean;
63 *
64 * public Foo() {
65 * _cloneableBean = new CloneableBean();
66 * }
67 *
68 * public Object clone() throws CloneNotSupportedException {
69 * return _cloneableBean.beanClone();
70 * }
71 *
72 * }
73 * </code>
74 * <p>
75 * @param obj object bean to clone.
76 *
77 */
78 public CloneableBean(Object obj) {
79 _obj = obj;
80 }
81
82 /***
83 * Makes a deep bean clone of the object.
84 * <p>
85 * To be used by classes extending CloneableBean. Although it works also for classes using
86 * CloneableBean in a delegation pattern, for correctness those classes should use the
87 * @see #beanClone() beanClone method.
88 * <p>
89 * @return a clone of the object bean.
90 * @throws CloneNotSupportedException thrown if the object bean could not be cloned.
91 *
92 */
93 public Object clone() throws CloneNotSupportedException {
94 return beanClone();
95 }
96
97 /***
98 * Makes a deep bean clone of the object passed in the constructor.
99 * <p>
100 * To be used by classes using CloneableBean in a delegation pattern,
101 * @see #CloneableBean(Object) constructor.
102 *
103 * @return a clone of the object bean.
104 * @throws CloneNotSupportedException thrown if the object bean could not be cloned.
105 *
106 */
107 public Object beanClone() throws CloneNotSupportedException {
108 Object clonedBean;
109 try {
110 clonedBean = _obj.getClass().newInstance();
111 BeanInfo bi = Introspector.getBeanInfo(_obj.getClass());
112 PropertyDescriptor[] pds = bi.getPropertyDescriptors();
113 if (pds!=null) {
114 for (int i=0;i<pds.length;i++) {
115 Method pReadMethod = pds[i].getReadMethod();
116 Method pWriteMethod = pds[i].getWriteMethod();
117 if (pReadMethod!=null && pWriteMethod!=null &&
118 pReadMethod.getDeclaringClass()!=Object.class &&
119 pReadMethod.getParameterTypes().length==0) {
120 Object value = pReadMethod.invoke(_obj,NO_PARAMS);
121 if (value!=null) {
122 value = doClone(value);
123 pWriteMethod.invoke(clonedBean,new Object[]{value});
124 }
125 }
126 }
127 }
128 }
129 catch (CloneNotSupportedException cnsEx) {
130 throw cnsEx;
131 }
132 catch (Exception ex) {
133 System.out.println(ex);
134 ex.printStackTrace(System.out);
135 throw new CloneNotSupportedException("Cannot clone a "+_obj.getClass()+" object");
136 }
137 return clonedBean;
138 }
139
140 private Object doClone(Object value) throws Exception {
141 if (value!=null) {
142 Class vClass = value.getClass();
143 if (vClass.isArray()) {
144 value = cloneArray(value);
145 }
146 else
147 if (value instanceof Collection) {
148 value = cloneCollection((Collection)value);
149 }
150 else
151 if (value instanceof Map) {
152 value = cloneMap((Map)value);
153 }
154 else
155 if (isBasicType(vClass)) {
156
157 }
158 else
159 if (value instanceof Cloneable) {
160 Method cloneMethod = vClass.getMethod("clone",NO_PARAMS_DEF);
161 if (Modifier.isPublic(cloneMethod.getModifiers())) {
162 value = cloneMethod.invoke(value,NO_PARAMS);
163 }
164 else {
165 throw new CloneNotSupportedException("Cannot clone a "+value.getClass()+" object, clone() is not public");
166 }
167 }
168 else {
169 throw new CloneNotSupportedException("Cannot clone a "+vClass.getName()+" object");
170 }
171 }
172 return value;
173 }
174
175 private Object cloneArray(Object array) throws Exception {
176 Class elementClass = array.getClass().getComponentType();
177 int length = Array.getLength(array);
178 Object newArray = Array.newInstance(elementClass,length);
179 for (int i=0;i<length;i++) {
180 Object element = doClone(Array.get(array,i));
181 Array.set(newArray,i,element);
182 }
183 return newArray;
184 }
185
186 private Object cloneCollection(Collection collection) throws Exception {
187 Class mClass = collection.getClass();
188 Collection newColl = (Collection) mClass.newInstance();
189 Iterator i = collection.iterator();
190 while (i.hasNext()) {
191 Object element = doClone(i.next());
192 newColl.add(element);
193 }
194 return newColl;
195 }
196
197 private Object cloneMap(Map map) throws Exception {
198 Class mClass = map.getClass();
199 Map newMap = (Map) mClass.newInstance();
200 Iterator entries = map.entrySet().iterator();
201 while (entries.hasNext()) {
202 Map.Entry entry = (Map.Entry) entries.next();
203 Object key = doClone(entry.getKey());
204 Object value = doClone(entry.getValue());
205 newMap.put(key,value);
206 }
207 return newMap;
208 }
209
210 private static final Set BASIC_TYPES = new HashSet();
211
212 static {
213 BASIC_TYPES.add(Boolean.class);
214 BASIC_TYPES.add(Byte.class);
215 BASIC_TYPES.add(Character.class);
216 BASIC_TYPES.add(Double.class);
217 BASIC_TYPES.add(Float.class);
218 BASIC_TYPES.add(Integer.class);
219 BASIC_TYPES.add(Long.class);
220 BASIC_TYPES.add(Short.class);
221 BASIC_TYPES.add(String.class);
222 }
223
224 private static final Map CONSTRUCTOR_BASIC_TYPES = new HashMap();
225
226 static {
227 CONSTRUCTOR_BASIC_TYPES.put(Boolean.class,new Class[]{Boolean.TYPE});
228 CONSTRUCTOR_BASIC_TYPES.put(Byte.class,new Class[]{Byte.TYPE});
229 CONSTRUCTOR_BASIC_TYPES.put(Character.class,new Class[]{Character.TYPE});
230 CONSTRUCTOR_BASIC_TYPES.put(Double.class,new Class[]{Double.TYPE});
231 CONSTRUCTOR_BASIC_TYPES.put(Float.class,new Class[]{Float.TYPE});
232 CONSTRUCTOR_BASIC_TYPES.put(Integer.class,new Class[]{Integer.TYPE});
233 CONSTRUCTOR_BASIC_TYPES.put(Long.class,new Class[]{Long.TYPE});
234 CONSTRUCTOR_BASIC_TYPES.put(Short.class,new Class[]{Short.TYPE});
235 CONSTRUCTOR_BASIC_TYPES.put(String.class,new Class[]{String.class});
236 }
237
238 private boolean isBasicType(Class vClass) {
239 return BASIC_TYPES.contains(vClass);
240 }
241
242
243 /***
244 private Object cloneBasicType(Object value) throws Exception {
245 Class pClass = value.getClass();
246 Class[] defType = (Class[]) CONSTRUCTOR_BASIC_TYPES.get(pClass);
247 Constructor cons = pClass.getDeclaredConstructor(defType);
248 value = cons.newInstance(new Object[]{value});
249 return value;
250 }
251 **/
252
253 }
254