1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.morph.transform.copiers;
17
18 import java.util.Arrays;
19 import java.util.Enumeration;
20 import java.util.Iterator;
21 import java.util.Locale;
22 import java.util.Map;
23 import java.util.Set;
24
25 import net.sf.composite.util.ObjectUtils;
26 import net.sf.morph.reflect.GrowableContainerReflector;
27 import net.sf.morph.reflect.IndexedContainerReflector;
28 import net.sf.morph.reflect.MutableIndexedContainerReflector;
29 import net.sf.morph.transform.DecoratedConverter;
30 import net.sf.morph.transform.DecoratedCopier;
31 import net.sf.morph.transform.NodeCopier;
32 import net.sf.morph.transform.TransformationException;
33 import net.sf.morph.transform.Transformer;
34 import net.sf.morph.transform.support.ResetableIteratorWrapper;
35 import net.sf.morph.transform.transformers.BaseReflectorTransformer;
36 import net.sf.morph.util.ContainerUtils;
37 import net.sf.morph.util.IteratorEnumeration;
38 import net.sf.morph.util.ReflectorUtils;
39 import net.sf.morph.util.TransformerUtils;
40 import net.sf.morph.util.TypeMap;
41
42 /**
43 * <p>
44 * Copies information from any container object to any object that has either a
45 * {@link GrowableContainerReflector}or a
46 * {@link MutableIndexedContainerReflector}. If the source object has a
47 * growable container reflector, information is added to the end of the
48 * destination when a copy is performed. If the source object does not have a
49 * growable container reflector (i.e. - it has a mutable indexed reflector),
50 * information copied from the source to the destination will overwrite the
51 * information in the destination.
52 * </p>
53 *
54 * <p>
55 * By default, this means:
56 * </p>
57 * <table border="1">
58 * <tr>
59 * <th align="left">Destinations</th>
60 * <th align="left">Sources</th>
61 * </tr>
62 * <tr>
63 * <td>
64 * java.util.List <br>
65 * java.util.Set <br>
66 * java.lang.Object[], int[], etc. (arrays)</td>
67 * <td>
68 * java.util.Iterator <br>
69 * java.util.Enumeration <br>
70 * java.util.Collection <br>
71 * java.lang.Object[], int[], etc. (arrays) <br>
72 * java.util.Map (values are extracted) <br>
73 * java.lang.Object (added to the end of the destination) <br>
74 * </td>
75 * </tr>
76 * </table>
77 *
78 * @author Matt Sgarlata
79 * @since Nov 27, 2004
80 */
81 public class ContainerCopier extends BaseReflectorTransformer implements DecoratedCopier,
82 DecoratedConverter, NodeCopier {
83
84
85 private Map containedSourceToDestinationTypeMap;
86 private boolean preferGrow = true;
87
88 /**
89 * Create a new ContainerCopier.
90 */
91 public ContainerCopier() {
92 super();
93 }
94
95 /**
96 * Determine the container element destination type. By default delegates to {@link #determineDestinationContainedType(Object, Class)}.
97 * @param destination container
98 * @param sourceValue source value
99 * @param sourceValueClass source type, or type source value would be if not null
100 * @return destination element type
101 */
102 protected Class determineDestinationContainedType(Object destination,
103 Object sourceValue, Class sourceValueClass, Locale locale) {
104 return determineDestinationContainedType(destination, sourceValueClass);
105 }
106
107 /**
108 * Determine the container element destination type.
109 * @param destination container
110 * @param sourceValueClass source type
111 * @return destination element type
112 * @deprecated in favor of fully-specified method
113 */
114 protected Class determineDestinationContainedType(Object destination,
115 Class sourceValueClass) {
116
117 Class destinationType = null;
118
119
120
121 destinationType = TransformerUtils.getMappedDestinationType(
122 containedSourceToDestinationTypeMap, sourceValueClass);
123
124
125 if (destinationType == null) {
126 Class candidateDestinationType = getContainerReflector().getContainedType(
127 destination.getClass());
128
129
130 if (candidateDestinationType != Object.class) {
131
132 destinationType = candidateDestinationType;
133 }
134 }
135
136 if (destinationType == null) {
137
138 Class[] availableDestinationTypes = TransformerUtils.getDestinationClasses(getNestedTransformer(), sourceValueClass);
139 if (availableDestinationTypes.length == 1 && availableDestinationTypes[0] != Object.class) {
140 destinationType = availableDestinationTypes[0];
141 }
142 }
143
144 if (destinationType == null) {
145
146 destinationType = sourceValueClass;
147 }
148 return destinationType;
149 }
150
151 /**
152 * Adds an element to the destination object that is from the given index of
153 * the source object.
154 *
155 * @param index
156 * the current index into the container that is being transformed
157 * @param destination
158 * the destination container to which values are being copied
159 * from the source container
160 * @param sourceValue
161 * the value the source object has at the current index
162 * @param sourceValueClass
163 * the type of sourceValue, or the type the sourceValue would be
164 * were it not <code>null</code>
165 * @param locale
166 * the locale in which the current transformation is taking place
167 * @param preferredTransformationType
168 * the preferred transformation type to perform when transforming
169 * the sourceValue for addition into the destination
170 */
171 protected void put(int index, Object destination, Object sourceValue, Class sourceValueClass, Locale locale, Integer preferredTransformationType) {
172 Class destinationContainedType =
173 determineDestinationContainedType(destination, sourceValue, sourceValueClass, locale);
174
175 boolean canGrow = ReflectorUtils.isReflectable(getReflector(),
176 destination.getClass(), GrowableContainerReflector.class);
177 boolean canMutate = ReflectorUtils.isReflectable(getReflector(),
178 destination.getClass(), MutableIndexedContainerReflector.class)
179 && getMutableIndexedContainerReflector().getSize(destination) > index;
180 Integer xform = null;
181
182 if (isPreferGrow() || TRANSFORMATION_TYPE_CONVERT.equals(preferredTransformationType)) {
183 xform = canGrow ? TRANSFORMATION_TYPE_CONVERT
184 : canMutate ? TRANSFORMATION_TYPE_COPY : null;
185 }
186 else if (TRANSFORMATION_TYPE_COPY.equals(preferredTransformationType)) {
187 xform = canMutate ? TRANSFORMATION_TYPE_COPY
188 : canGrow ? TRANSFORMATION_TYPE_CONVERT : null;
189 }
190
191 if (TRANSFORMATION_TYPE_CONVERT.equals(xform)) {
192
193
194
195 Object convertedValue = nestedTransform(destinationContainedType, null,
196 sourceValue, locale, TRANSFORMATION_TYPE_CONVERT);
197
198 getGrowableContainerReflector().add(destination, convertedValue);
199 }
200
201
202 else if (TRANSFORMATION_TYPE_COPY.equals(xform)) {
203
204
205
206
207 Object destinationValue = getMutableIndexedContainerReflector().get(
208 destination, index);
209 Object transformedValue = nestedTransform(destinationContainedType,
210 destinationValue, sourceValue, locale, preferredTransformationType);
211 getMutableIndexedContainerReflector().set(destination, index,
212 transformedValue);
213 }
214 else {
215
216 throw new TransformationException("Unable to copy value at index "
217 + index + " to the destination because "
218 + ObjectUtils.getObjectDescription(getReflector())
219 + ", the reflector specified for "
220 + ObjectUtils.getObjectDescription(this)
221 + ", cannot reflect destination "
222 + ObjectUtils.getObjectDescription(destination)
223 + " with a reflector that implements "
224 + GrowableContainerReflector.class.getName() + " or "
225 + MutableIndexedContainerReflector.class.getName());
226 }
227 }
228
229 /**
230 * Do a nested transformation.
231 * @param destinationContainedType
232 * @param destinationValue
233 * @param sourceValue
234 * @param locale
235 * @param preferredTransformationType
236 * @return result
237 */
238 protected Object nestedTransform(Class destinationContainedType,
239 Object destinationValue, Object sourceValue, Locale locale,
240 Integer preferredTransformationType) {
241 return TransformerUtils.transform(getNestedTransformer(),
242 destinationContainedType, destinationValue, sourceValue, locale,
243 preferredTransformationType);
244 }
245
246 /**
247 * {@inheritDoc}
248 */
249 protected Object convertImpl(Class destinationClass, Object source, Locale locale) throws Exception {
250
251
252
253
254
255
256
257
258
259 boolean iter = Iterator.class.isAssignableFrom(destinationClass);
260 if (iter || Enumeration.class.isAssignableFrom(destinationClass)) {
261
262
263
264 Iterator iterator = getContainerReflector().getIterator(source);
265 return iter ? (Object) iterator : new IteratorEnumeration(iterator);
266 }
267 return super.convertImpl(destinationClass, source, locale);
268 }
269
270 /**
271 * {@inheritDoc}
272 */
273 protected void copyImpl(Object destination, Object source, Locale locale, Integer preferredTransformationType)
274 throws TransformationException {
275
276
277
278 if (destination instanceof Iterator ||
279 destination instanceof Enumeration) {
280 return;
281 }
282 int i = 0;
283 Iterator sourceIterator = getContainerReflector().getIterator(source);
284 while (sourceIterator.hasNext()) {
285 Object sourceValue = sourceIterator.next();
286
287 Class sourceValueClass;
288 if (sourceValue == null) {
289 sourceValueClass = getContainerReflector().getContainedType(
290 source.getClass());
291 }
292 else {
293 sourceValueClass = sourceValue.getClass();
294 }
295 put(i++, destination, sourceValue, sourceValueClass, locale,
296 preferredTransformationType);
297 }
298 }
299
300 /**
301 * {@inheritDoc}
302 */
303 public Object createReusableSource(Class destinationClass, Object source) {
304
305 return destinationClass.isArray() ? new ResetableIteratorWrapper(
306 getContainerReflector().getIterator(source))
307 : super.createReusableSource(destinationClass, source);
308 }
309
310 /**
311 * {@inheritDoc}
312 */
313 protected Class[] getDestinationClassesImpl() throws Exception {
314 Set set = ContainerUtils.createOrderedSet();
315 if (hasReflector(GrowableContainerReflector.class)) {
316 set.addAll(Arrays.asList(getGrowableContainerReflector().getReflectableClasses()));
317 }
318 if (hasReflector(IndexedContainerReflector.class)) {
319 set.addAll(Arrays.asList(getIndexedContainerReflector().getReflectableClasses()));
320 }
321 set.add(Iterator.class);
322 set.add(Enumeration.class);
323 return (Class[]) set.toArray(new Class[set.size()]);
324 }
325
326 /**
327 * {@inheritDoc}
328 */
329 protected Class[] getSourceClassesImpl() throws Exception {
330 return getContainerReflector().getReflectableClasses();
331 }
332
333 /**
334 * {@inheritDoc}
335 */
336 protected boolean isWrappingRuntimeExceptions() {
337
338
339 return false;
340 }
341
342 /**
343 * {@inheritDoc}
344 */
345 public Transformer getNestedTransformer() {
346 return super.getNestedTransformer();
347 }
348
349 /**
350 * {@inheritDoc}
351 */
352 public void setNestedTransformer(Transformer transformer) {
353 super.setNestedTransformer(transformer);
354 }
355
356 /**
357 * Get the mapping of source to destination container element types.
358 * @return Map
359 * @see {@link net.sf.morph.util.TypeMap}
360 */
361 public Map getContainedSourceToDestinationTypeMap() {
362 return containedSourceToDestinationTypeMap;
363 }
364
365 /**
366 * Set the mapping of source to destination container element types.
367 * @param containedSourceToDestinationMapping Map
368 * @see {@link net.sf.morph.util.TypeMap}
369 */
370 public void setContainedSourceToDestinationTypeMap(
371 Map containedSourceToDestinationMapping) {
372 this.containedSourceToDestinationTypeMap = new TypeMap(containedSourceToDestinationMapping);
373 }
374
375 /**
376 * Learn whether this ContainerCopier prefers to grow the destination when possible.
377 * @return boolean
378 * @since Morph 1.1
379 */
380 public boolean isPreferGrow() {
381 return preferGrow;
382 }
383
384 /**
385 * Set whether this ContainerCopier prefers to grow the destination when possible.
386 * Backward-compatible default setting is <code>true</code>; when <code>false</code>
387 * grow vs. mutate will be determined at runtime by the capabilities of the configured
388 * reflector with regard to the copy destination; preferredTransformationType will also
389 * be observed.
390 * @see #copyImpl(Object, Object, Locale, Integer)
391 * @since Morph 1.1
392 * @param preferGrow the boolean to set
393 */
394 public void setPreferGrow(boolean preferGrow) {
395 this.preferGrow = preferGrow;
396 }
397 }