1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.morph.transform.transformers;
17
18 import java.util.Arrays;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.Locale;
22 import java.util.Map;
23 import java.util.Set;
24
25 import net.sf.composite.CompositeException;
26 import net.sf.composite.SpecializableComposite;
27 import net.sf.composite.specialize.Specializer;
28 import net.sf.composite.specialize.specializers.CachingSpecializerProxy;
29 import net.sf.composite.specialize.specializers.CloningSpecializer;
30 import net.sf.composite.util.ObjectPair;
31 import net.sf.composite.util.ObjectUtils;
32 import net.sf.morph.transform.Converter;
33 import net.sf.morph.transform.Copier;
34 import net.sf.morph.transform.DecoratedConverter;
35 import net.sf.morph.transform.DecoratedCopier;
36 import net.sf.morph.transform.ExplicitTransformer;
37 import net.sf.morph.transform.ImpreciseTransformer;
38 import net.sf.morph.transform.NodeCopier;
39 import net.sf.morph.transform.TransformationException;
40 import net.sf.morph.transform.Transformer;
41 import net.sf.morph.transform.converters.DefaultToBooleanConverter;
42 import net.sf.morph.transform.converters.DefaultToTextConverter;
43 import net.sf.morph.transform.converters.IdentityConverter;
44 import net.sf.morph.transform.converters.NumberConverter;
45 import net.sf.morph.transform.converters.NumberToTimeConverter;
46 import net.sf.morph.transform.converters.ObjectToClassConverter;
47 import net.sf.morph.transform.converters.TextConverter;
48 import net.sf.morph.transform.converters.TextToNumberConverter;
49 import net.sf.morph.transform.converters.TextToTimeConverter;
50 import net.sf.morph.transform.converters.TimeConverter;
51 import net.sf.morph.transform.converters.TimeToNumberConverter;
52 import net.sf.morph.transform.copiers.ContainerCopier;
53 import net.sf.morph.transform.copiers.ImmutableComponentArrayCopier;
54 import net.sf.morph.transform.copiers.MapCopier;
55 import net.sf.morph.transform.copiers.PropertyNameMatchingCopier;
56 import net.sf.morph.transform.copiers.TextToContainerCopier;
57 import net.sf.morph.util.ClassUtils;
58 import net.sf.morph.util.ContainerUtils;
59 import net.sf.morph.util.MutableInteger;
60 import net.sf.morph.util.TransformerUtils;
61
62 /**
63 * <p>
64 * Delegates transformations to a list of transformers. The transformers are
65 * tried in the order in which they appear in the <code>components</code>
66 * property of this transformer.
67 * </p>
68 *
69 * <p>
70 * By default this transformer is initialized with a set of transformers that
71 * will meet basic needs. This list of transformers is subject to change in
72 * future versions of Morph, but we do not anticipate removing transformers,
73 * only adding new ones or modifying existing ones. This means if a
74 * transformation works in an older version of Morph it will also work in a
75 * newer version. A transformation's behavior may in some cases change between
76 * releases, but we will avoid this whenever possible.
77 * </p>
78 *
79 * <p>
80 * The default set of transformers includes both converters and copiers. For
81 * calls to copy methods, only the copiers will be used. For calls to convert
82 * methods, the converters <em>and</em> the copiers will be used, since all
83 * Copiers provided by the Morph framework are also Converters.
84 * </p>
85 *
86 * <p>
87 * Any delegates which implement
88 * {@link net.sf.morph.transform.NodeCopier} will automatically have this
89 * transformer marked as the parent transformer. This is important for
90 * performing deep copies of object graphs. Note that it is safe for a SimpleDelegatingTransformer
91 * to implement the NodeCopier interface <em>only</em> if all its components do so.
92 * </p>
93 *
94 * @author Matt Sgarlata
95 * @since Dec 12, 2004
96 */
97 public class SimpleDelegatingTransformer extends BaseCompositeTransformer implements
98 SpecializableComposite, ExplicitTransformer, Transformer, DecoratedCopier,
99 DecoratedConverter, Cloneable, ImpreciseTransformer {
100
101
102
103 private static class MapThreadLocal extends ThreadLocal {
104 protected Object initialValue() {
105 return new HashMap();
106 }
107 }
108
109 private static class StackDepthThreadLocal extends ThreadLocal {
110 protected Object initialValue() {
111 return new MutableInteger();
112 }
113 }
114
115 /**
116 * Create the default set of Transformer components.
117 * @return Transformer[]
118 */
119 protected Transformer[] createDefaultComponents() {
120 return new Transformer[] {
121 new DefaultToBooleanConverter(),
122 new IdentityConverter(),
123 new ObjectToClassConverter(),
124 new TextConverter(),
125 new DefaultToTextConverter(),
126 new TextToNumberConverter(),
127 new TextToTimeConverter(),
128 new NumberToTimeConverter(),
129 new TimeToNumberConverter(),
130 new NumberConverter(),
131 new TimeConverter(),
132 new TextToContainerCopier(),
133 new MapCopier(),
134 new PropertyNameMatchingCopier() {
135 { setDestinationClasses(new Class[] { Map.class }); }
136 },
137 new ImmutableComponentArrayCopier(),
138 new ContainerCopier(),
139 new PropertyNameMatchingCopier()
140 };
141 }
142
143 private Specializer specializer;
144 private boolean preferPreciseTransformers;
145
146 private transient ThreadLocal visitedSourceToDestinationMapThreadLocal = new MapThreadLocal();
147 private transient ThreadLocal stackDepthThreadLocal = new StackDepthThreadLocal();
148
149 private transient Map copierRegistry = Collections.synchronizedMap(new HashMap());
150 private transient Map transformerRegistry = Collections.synchronizedMap(new HashMap());
151
152 /**
153 * Construct a new SimpleDelegatingTransformer.
154 */
155 public SimpleDelegatingTransformer() {
156 super();
157 }
158
159 /**
160 * Construct a new SimpleDelegatingTransformer.
161 * @param components
162 */
163 public SimpleDelegatingTransformer(Transformer[] components) {
164 this(components, false);
165 }
166
167 /**
168 * Construct a new SimpleDelegatingTransformer.
169 * @param components
170 * @param appendDefaultComponents
171 */
172 public SimpleDelegatingTransformer(Transformer[] components, boolean appendDefaultComponents) {
173 if (appendDefaultComponents) {
174 Transformer[] defaultComponents = createDefaultComponents();
175 if (ObjectUtils.isEmpty(components)) {
176 components = defaultComponents;
177 }
178 else {
179 Transformer[] newComponents = (Transformer[]) ClassUtils.createArray(
180 getComponentType(), components.length + defaultComponents.length);
181 System.arraycopy(components, 0, newComponents, 0, components.length);
182 System.arraycopy(defaultComponents, 0, newComponents, components.length, defaultComponents.length);
183 components = newComponents;
184 }
185 }
186 setComponents(components);
187 }
188
189 /**
190 * {@inheritDoc}
191 * @see net.sf.morph.transform.transformers.BaseCompositeTransformer#initializeImpl()
192 */
193 protected void initializeImpl() throws Exception {
194 super.initializeImpl();
195 if (getNestedTransformer() == null) {
196 setNestedTransformer(this);
197 }
198 }
199
200 /**
201 * Determines if one of the delegate transformers is capable of performing
202 * the given transformation. This method is necessary because otherwise the
203 * delegating transformer would be too eager in what it says it can convert.
204 * For example, if a delegate transformer was capable of transforming A to B
205 * and another delegate was capable of transforming C to D, this transformer
206 * would incorrectly state that transforming A to D and C to B was possible
207 * even though they are not.
208 *
209 * @param destinationType
210 * the destination type to test
211 * @param sourceType
212 * the source type to test
213 * @return whether the destination type is transformable to the source type
214 * @throws TransformationException
215 * if it could not be determined if <code>sourceType</code> is
216 * transformable into <code>destinationType</code>
217 */
218 protected boolean isTransformableImpl(Class destinationType,
219 Class sourceType) throws Exception {
220 for (int i = 0; i < getTransformers().length; i++) {
221 Transformer transformer = getTransformers()[i];
222 if (TransformerUtils.isTransformable(transformer,
223 destinationType, sourceType)) {
224 return true;
225 }
226 }
227 return false;
228 }
229
230 /**
231 * {@inheritDoc}
232 */
233 protected boolean isImpreciseTransformationImpl(Class destinationClass, Class sourceClass) {
234 return TransformerUtils.isImpreciseTransformation(getTransformer(destinationClass,
235 sourceClass), destinationClass, sourceClass);
236 }
237
238 /**
239 * {@inheritDoc}
240 * @see net.sf.morph.transform.transformers.BaseTransformer#getSourceClassesImpl()
241 */
242 protected Class[] getSourceClassesImpl() throws Exception {
243 Set sourceClasses = ContainerUtils.createOrderedSet();
244 Transformer[] t = getTransformers();
245 for (int i = 0; i < t.length; i++) {
246 sourceClasses.addAll(Arrays.asList(t[i].getSourceClasses()));
247 }
248 return (Class[]) sourceClasses.toArray(new Class[sourceClasses.size()]);
249 }
250
251 /**
252 * {@inheritDoc}
253 * @see net.sf.morph.transform.transformers.BaseTransformer#getDestinationClassesImpl()
254 */
255 protected Class[] getDestinationClassesImpl() throws Exception {
256 Set destinationClasses = ContainerUtils.createOrderedSet();
257 Transformer[] t = getTransformers();
258 for (int i = 0; i < t.length; i++) {
259 destinationClasses.addAll(Arrays.asList(t[i].getDestinationClasses()));
260 }
261 return (Class[]) destinationClasses.toArray(new Class[destinationClasses.size()]);
262 }
263
264 /**
265 * {@inheritDoc}
266 * @see net.sf.morph.transform.transformers.BaseTransformer#copyImpl(java.lang.Object, java.lang.Object, java.util.Locale, java.lang.Integer)
267 */
268 protected void copyImpl(Object destination, Object source, Locale locale, Integer preferredTransformationType)
269 throws Exception {
270 incrementStackDepth();
271 try {
272 if (!hasVisitedDestination(source, destination)) {
273 Class destinationType = ClassUtils.getClass(destination);
274 Copier copier = getCopier(destinationType, ClassUtils.getClass(source));
275 recordVisit(source, destinationType, destination);
276 copier.copy(destination, source, locale);
277 }
278 } finally {
279 decrementStackDepth();
280 clearVisitedSourceToDestinationMapIfNecessary();
281 }
282 }
283
284 /**
285 * {@inheritDoc}
286 * @see net.sf.morph.transform.transformers.BaseTransformer#convertImpl(java.lang.Class, java.lang.Object, java.util.Locale)
287 */
288 protected Object convertImpl(Class destinationType, Object source, Locale locale)
289 throws Exception {
290 incrementStackDepth();
291 try {
292 Class sourceClass = ClassUtils.getClass(source);
293 Transformer transformer = getTransformer(destinationType, sourceClass);
294
295 if (hasVisited(source, destinationType)) {
296 return getCachedResult(source, destinationType);
297 }
298 if (transformer instanceof NodeCopier) {
299 NodeCopier nodeCopier = (NodeCopier) transformer;
300 Object reuseableSource = nodeCopier.createReusableSource(destinationType, source);
301 Object newInstance = nodeCopier.createNewInstance(destinationType,
302 reuseableSource);
303 recordVisit(source, destinationType, newInstance);
304 nodeCopier.copy(newInstance, reuseableSource, locale);
305 return newInstance;
306 }
307 Converter converter = (Converter) transformer;
308 return converter.convert(destinationType, source, locale);
309 } finally {
310 decrementStackDepth();
311 clearVisitedSourceToDestinationMapIfNecessary();
312 }
313 }
314
315 /**
316 * Increment the depth of the nested copy stack.
317 */
318 protected void incrementStackDepth() {
319 ((MutableInteger) stackDepthThreadLocal.get()).value++;
320 }
321
322 /**
323 * Decrement the depth of the nested copy stack.
324 */
325 protected void decrementStackDepth() {
326 ((MutableInteger) stackDepthThreadLocal.get()).value--;
327 }
328
329 /**
330 * If we have popped everybody off the stack, clear the cache.
331 */
332 protected void clearVisitedSourceToDestinationMapIfNecessary() {
333 if (((MutableInteger) stackDepthThreadLocal.get()).value == 0) {
334 getVisitedSourceToDestinationMap().clear();
335 }
336 }
337
338 /**
339 * Cache destination object for this source/destination class combination.
340 * @param source
341 * @param destinationType
342 * @param destination
343 */
344 protected void recordVisit(Object source, Class destinationType, Object destination) {
345 Object key = new ObjectPair(source, destinationType);
346 getVisitedSourceToDestinationMap().put(key, destination);
347 }
348
349 /**
350 * Avoid recursion by checking whether we have already begun a conversion
351 * of the specified source object to the specified destination class.
352 * @param source
353 * @param destinationType
354 * @return boolean
355 */
356 protected boolean hasVisited(Object source, Class destinationType) {
357 Object key = new ObjectPair(source, destinationType);
358 return getVisitedSourceToDestinationMap().containsKey(key);
359 }
360
361 /**
362 * Avoid recursion by checking whether we have already begun a copy
363 * of the specified source object to the specified destination object.
364 * @param source
365 * @param destination
366 * @return boolean
367 */
368 protected boolean hasVisitedDestination(Object source, Object destination) {
369
370
371 Class destinationType = ClassUtils.getClass(destination);
372 return hasVisited(source, destinationType)
373 && getCachedResult(source, destinationType) == destination;
374 }
375
376 /**
377 * Get the cached object in the process of being transformed
378 * @param source
379 * @param destinationType
380 * @return
381 */
382 protected Object getCachedResult(Object source, Class destinationType) {
383 Object key = new ObjectPair(source, destinationType);
384 if (!getVisitedSourceToDestinationMap().containsKey(key)) {
385 throw new IllegalArgumentException(
386 "Cannot return a cached conversion result for "
387 + ObjectUtils.getObjectDescription(source)
388 + " to destination type '"
389 + destinationType
390 + "' because that conversion hasn't been performed before");
391 }
392 return getVisitedSourceToDestinationMap().get(key);
393 }
394
395 /**
396 * {@inheritDoc}
397 * @see net.sf.composite.SpecializableComposite#specialize(java.lang.Class)
398 */
399 public Object specialize(Class compositeType) {
400 return getSpecializer().specialize(this, compositeType);
401 }
402
403 /**
404 * {@inheritDoc}
405 * @see net.sf.composite.SpecializableComposite#isSpecializable(java.lang.Class)
406 */
407 public boolean isSpecializable(Class type) throws CompositeException {
408 return getSpecializer().isSpecializable(this, type);
409 }
410
411 /**
412 * Finds a transformer of type <code>transformerType</code> that is
413 * capable of transforming <code>sourceClass</code> to
414 * <code>destinationClass</code>. Caches results in the
415 * <code>registry</code>.
416 *
417 * @param registry
418 * a cache that remembers which transformers can be used for
419 * which transformations
420 * @param transformerType
421 * the type of the returned transformer
422 * @param destinationClass
423 * the destinationClass of the transformation
424 * @param sourceClass
425 * the sourceClass of the transformation
426 * @return the transformer of the requested type capable of performing the
427 * requested transformation
428 * @throws TransformationException
429 * if no suitable transformer could be found
430 */
431 protected Transformer getTransformer(Map registry, Class transformerType, Class destinationClass, Class sourceClass) {
432 ObjectPair key = new ObjectPair(destinationClass, sourceClass);
433 Transformer transformer = (Transformer) registry.get(key);
434 if (transformer == null) {
435 transformer = getTransformer(transformerType, destinationClass, sourceClass);
436 registry.put(key, transformer);
437 }
438 return transformer;
439 }
440
441 /**
442 * Finds a Copier that is capable of transforming <code>sourceClass</code>
443 * to <code>destinationClass</code>.
444 *
445 * @param destinationClass
446 * the destinationClass of the transformation
447 * @param sourceClass
448 * the sourceClass of the transformation
449 * @return the transformer of the requested type capable of performing the
450 * requested transformation
451 * @throws TransformationException
452 * if no suitable copier could be found
453 */
454 protected Copier getCopier(Class destinationClass, Class sourceClass) {
455 return (Copier) getTransformer(copierRegistry, Copier.class, destinationClass, sourceClass);
456 }
457
458 /**
459 * Finds a Transformer that is capable of transforming
460 * <code>sourceClass</code> to <code>destinationClass</code>.
461 *
462 * @param destinationClass
463 * the destinationClass of the transformation
464 * @param sourceClass
465 * the sourceClass of the transformation
466 * @return the transformer of the requested type capable of performing the
467 * requested transformation
468 * @throws TransformationException
469 * if no suitable transformer could be found
470 */
471 protected Transformer getTransformer(Class destinationClass, Class sourceClass) {
472 return getTransformer(transformerRegistry, Transformer.class, destinationClass, sourceClass);
473 }
474
475 /**
476 * Finds a transformer of type <code>transformerType</code> that is
477 * capable of transforming <code>sourceClass</code> to
478 * <code>destinationClass</code>.
479 *
480 * @param transformerType
481 * the type of the returned transformer
482 * @param destinationClass
483 * the destinationClass of the transformation
484 * @param sourceClass
485 * the sourceClass of the transformation
486 * @return the transformer of the requested type capable of performing the
487 * requested transformation
488 * @throws TransformationException
489 * if no suitable transformer could be found
490 */
491 private Transformer getTransformer(Class transformerType, Class destinationClass, Class sourceClass) throws TransformationException {
492 Transformer candidate = null;
493 for (int i = 0; i < components.length; i++) {
494
495 Transformer transformer = (Transformer) components[i];
496 if (transformerType.isAssignableFrom(transformer.getClass())) {
497
498 if (TransformerUtils.isTransformable(
499 transformer, destinationClass, sourceClass)) {
500 if (isPreferPreciseTransformers()
501 && candidate == null
502 && TransformerUtils.isImpreciseTransformation(transformer,
503 destinationClass, sourceClass)) {
504 candidate = transformer;
505 continue;
506 }
507 if (getLog().isTraceEnabled()) {
508 getLog().trace("Using "
509 + ClassUtils.getUnqualifiedClassName(transformerType)
510 + " " + transformer.getClass().getName()
511 + " to transform "
512 + ObjectUtils.getObjectDescription(sourceClass)
513 + " to "
514 + ObjectUtils.getObjectDescription(destinationClass));
515 }
516 return transformer;
517 }
518 }
519 if (candidate != null) {
520 return candidate;
521 }
522 }
523 throw new TransformationException(
524 "Could not find a transformer that can transform objects of "
525 + ObjectUtils.getObjectDescription(sourceClass)
526 + " to objects of "
527 + ObjectUtils.getObjectDescription(destinationClass));
528 }
529
530 /**
531 * Get our components as a typesafe Transformer array.
532 * @return Transformer[]
533 */
534 public synchronized Transformer[] getTransformers() {
535 return (Transformer[]) getComponents();
536 }
537
538
539
540
541
542 public synchronized Object[] getComponents() {
543 if (components == null) {
544 setComponents(createDefaultComponents());
545 }
546 return super.getComponents();
547 }
548
549
550
551
552
553 public synchronized void setComponents(Object[] components) {
554 if (this.components == components) {
555 return;
556 }
557 this.components = components;
558 transformerRegistry.clear();
559 copierRegistry.clear();
560
561 if (components != null) {
562 updateNestedTransformerComponents(getNestedTransformer(), null);
563 }
564 }
565
566 /**
567 * Let the delegate do the logging
568 */
569 protected boolean isPerformingLogging() {
570 return false;
571 }
572
573 /**
574 * {@inheritDoc}
575 * @see net.sf.morph.transform.transformers.BaseTransformer#isAutomaticallyHandlingNulls()
576 */
577 protected boolean isAutomaticallyHandlingNulls() {
578 return false;
579 }
580
581 /**
582 * {@inheritDoc}
583 * @see net.sf.morph.transform.transformers.BaseTransformer#clone()
584 */
585 public Object clone() throws CloneNotSupportedException {
586 SimpleDelegatingTransformer result = (SimpleDelegatingTransformer) super.clone();
587 result.copierRegistry = Collections.synchronizedMap(new HashMap());
588 result.transformerRegistry = Collections.synchronizedMap(new HashMap());
589 result.visitedSourceToDestinationMapThreadLocal = new MapThreadLocal();
590 result.stackDepthThreadLocal = new StackDepthThreadLocal();
591 return result;
592 }
593
594 /**
595 * Gets a Map of all the nodes in the object graph that have been
596 * transformed so far by this transformer. The keys in the Map are the
597 * visited source nodes, and the values are the converted representation of
598 * the node.
599 *
600 * @return a Map of all the nodes in the object graph that have been
601 * transformed so far by this transformer
602 */
603 protected Map getVisitedSourceToDestinationMap() {
604 return (Map) visitedSourceToDestinationMapThreadLocal.get();
605 }
606
607 /**
608 * Get the Specializer for this SDT.
609 * @return Specializer
610 */
611 public Specializer getSpecializer() {
612 if (specializer == null) {
613 specializer = new CachingSpecializerProxy(new CloningSpecializer());
614 }
615 return specializer;
616 }
617
618 /**
619 * Set the Specializer for this SDT.
620 * @param specializer
621 */
622 public void setSpecializer(Specializer specializer) {
623 this.specializer = specializer;
624 }
625
626 /**
627 * {@inheritDoc}
628 * @see net.sf.morph.transform.transformers.BaseTransformer#createReusableSource(java.lang.Class, java.lang.Object)
629 */
630 protected Object createReusableSource(Class destinationClass, Object source) {
631 Transformer t = getTransformer(destinationClass, ClassUtils.getClass(source));
632 return t instanceof NodeCopier ? ((NodeCopier) t).createReusableSource(
633 destinationClass, source) : super.createReusableSource(destinationClass,
634 source);
635 }
636
637 /**
638 * Get the preferPreciseTransformers.
639 * @return boolean
640 */
641 public boolean isPreferPreciseTransformers() {
642 return preferPreciseTransformers;
643 }
644
645 /**
646 * Set the preferPreciseTransformers. Default false.
647 * @param preferPreciseTransformers the boolean to set
648 */
649 public void setPreferPreciseTransformers(boolean preferPreciseTransformers) {
650 this.preferPreciseTransformers = preferPreciseTransformers;
651 }
652
653 }