1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.foundation.internal;
20
21 import net.wotonomy.foundation.*;
22 import net.wotonomy.foundation.internal.Introspector;
23 import net.wotonomy.foundation.internal.WotonomyException;
24
25 import java.io.*;
26 import java.util.*;
27
28 /***
29 * Duplicator makes use of Introspector to duplicate objects,
30 * either by shallow copy, deep copy, or by copying properties
31 * from one object to apply to another object. You may find this
32 * class useful because java.lang.Object.clone() only supports
33 * shallow copying.
34 *
35 * @author michael@mpowers.net
36 * @author $Author: cgruber $
37 * @version $Revision: 895 $
38 */
39
40 public class Duplicator
41 {
42 /***
43 * Used to represent null values for properties in the
44 * maps returned by readProperties and cloneProperties
45 * and in the parameter to writeProperties.
46 * This actually references the NSNull instance.
47 */
48 public static final Object NULL = NSNull.nullValue();
49 private static NSSelector clone = new NSSelector( "clone" );
50
51 /***
52 * Returns a list of properties for the specified class
53 * that are both readable and writable.
54 */
55 static public List editablePropertiesForObject(
56 Object anObject )
57 {
58 List readProperties = new ArrayList();
59 String[] read = Introspector.getReadPropertiesForObject( anObject );
60 for ( int i = 0; i < read.length; i++ )
61 {
62 readProperties.add( read[i] );
63 }
64
65 List properties = new ArrayList();
66 String[] write = Introspector.getWritePropertiesForObject( anObject );
67 for ( int i = 0; i < write.length; i++ )
68 {
69 properties.add( write[i] );
70 }
71
72
73 properties.retainAll( readProperties );
74
75 return properties;
76 }
77
78 /***
79 * Returns a Map containing only the mutable properties
80 * for the specified object and their values.
81 * Any null values for properties will be represented with
82 * the NULL object.
83 */
84 static public Map readPropertiesForObject(
85 Object anObject )
86 {
87 NSMutableDictionary result = new NSMutableDictionary();
88
89 String key;
90 Object value;
91 Iterator it = editablePropertiesForObject( anObject ).iterator();
92 while ( it.hasNext() )
93 {
94 key = it.next().toString();
95 value = Introspector.get( anObject, key );
96 if ( value == null ) value = NULL;
97 result.setObjectForKey( value, key );
98 }
99 return result;
100 }
101
102 /***
103 * Returns a Map containing only the mutable properties
104 * for the specified object and deep clones of their values.
105 * Nulls are represented by the NULL object.
106 */
107 static public Map clonePropertiesForObject(
108 Object anObject )
109 {
110 Object key, value;
111 Map result = readPropertiesForObject( anObject );
112 Iterator it = result.keySet().iterator();
113 while ( it.hasNext() )
114 {
115 key = it.next();
116 value = result.get( key );
117 value = deepClone( value );
118 result.put( key, value );
119 }
120 return result;
121 }
122
123 /***
124 * Applies the map of properties and values to the
125 * specified object. Null values for properties must
126 * be represented by the NULL object.
127 */
128 static public void writePropertiesForObject(
129 Map aMap, Object anObject )
130 {
131 String key;
132 Object value;
133 Iterator it = aMap.keySet().iterator();
134 while ( it.hasNext() )
135 {
136 key = it.next().toString();
137 value = aMap.get( key );
138 if ( NULL.equals( value ) ) value = null;
139 Introspector.set( anObject, key, value );
140 }
141 }
142
143 /***
144 * Creates a new copy of the specified object.
145 * This implementation tries to call clone(),
146 * and failing that, calls newInstance
147 * and then calls copy() to transfer the values.
148 * @throws WotonomyException if any operation fails.
149 */
150 static public Object clone(
151 Object aSource )
152 {
153 Object result = null;
154 if ( clone.implementedByObject( aSource ) )
155 {
156 try
157 {
158 result = clone.invoke( aSource );
159 return result;
160 }
161 catch ( Exception exc )
162 {
163
164 }
165 }
166
167 Class c = aSource.getClass();
168 try
169 {
170 result = c.newInstance();
171 }
172 catch ( Exception exc )
173 {
174 throw new WotonomyException( exc );
175 }
176 return copy( aSource, result );
177 }
178
179 /***
180 * Creates a deep copy of the specified object.
181 * Every object in this objects graph will be
182 * duplicated with new instances.
183 * @throws WotonomyException if any operation fails.
184 */
185 static public Object deepClone(
186 Object aSource )
187 {
188
189
190
191 try
192 {
193 ByteArrayOutputStream byteOutput =
194 new ByteArrayOutputStream();
195 ObjectOutputStream objectOutput =
196 new ObjectOutputStream( byteOutput );
197
198 objectOutput.writeObject( aSource );
199 objectOutput.flush();
200 objectOutput.close();
201
202 ByteArrayInputStream byteInput =
203 new ByteArrayInputStream( byteOutput.toByteArray() );
204 ObjectInputStream objectInput =
205 new ObjectInputStream( byteInput );
206 return objectInput.readObject();
207 }
208 catch ( Exception exc )
209 {
210 throw new WotonomyException( "Error cloning object: " + aSource, exc );
211 }
212 }
213
214 /***
215 * Copies values from one object to another.
216 * Returns the destination object.
217 * @throws WotonomyException if any operation fails.
218 */
219 static public Object copy(
220 Object aSource, Object aDestination )
221 {
222 try
223 {
224 writePropertiesForObject(
225 readPropertiesForObject( aSource ), aDestination );
226 }
227 catch ( RuntimeException exc )
228 {
229 throw new WotonomyException( exc );
230 }
231 return aDestination;
232 }
233
234 /***
235 * Deeply clones the values from one object and applies them
236 * to another object.
237 * Returns the destination object.
238 * @throws WotonomyException if any operation fails.
239 */
240 static public Object deepCopy(
241 Object aSource, Object aDestination )
242 {
243 try
244 {
245 writePropertiesForObject(
246 clonePropertiesForObject( aSource ), aDestination );
247 }
248 catch ( RuntimeException exc )
249 {
250 throw new WotonomyException( exc );
251 }
252 return aDestination;
253 }
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299