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.ArrayList;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Locale;
22
23 import net.sf.composite.util.ObjectUtils;
24 import net.sf.morph.transform.Converter;
25 import net.sf.morph.transform.Copier;
26 import net.sf.morph.transform.DecoratedConverter;
27 import net.sf.morph.transform.DecoratedCopier;
28 import net.sf.morph.transform.ExplicitTransformer;
29 import net.sf.morph.transform.ImpreciseTransformer;
30 import net.sf.morph.transform.TransformationException;
31 import net.sf.morph.transform.Transformer;
32 import net.sf.morph.transform.copiers.CopierDecorator;
33 import net.sf.morph.util.Assert;
34 import net.sf.morph.util.ClassUtils;
35 import net.sf.morph.util.TransformerUtils;
36
37 /**
38 * Runs one or more transformers in a chain.
39 *
40 * @author Matt Sgarlata
41 * @author Matt Benson
42 * @since Nov 24, 2004
43 */
44 public class ChainedTransformer extends BaseCompositeTransformer implements
45 DecoratedConverter, DecoratedCopier, ExplicitTransformer, ImpreciseTransformer {
46
47 private Converter copyConverter;
48
49 /**
50 * Create a new ChainedTransformer.
51 */
52 public ChainedTransformer() {
53 }
54
55 /**
56 * Create a new ChainedTransformer.
57 * @param chain
58 */
59 public ChainedTransformer(Transformer[] chain) {
60 setComponents(chain);
61 }
62
63 /**
64 * {@inheritDoc}
65 * @see net.sf.morph.transform.transformers.BaseTransformer#isTransformableImpl(java.lang.Class, java.lang.Class)
66 */
67 protected boolean isTransformableImpl(Class destinationType, Class sourceType) throws Exception {
68 return getConversionPath(destinationType, sourceType) != null;
69 }
70
71 /**
72 * {@inheritDoc}
73 */
74 protected boolean isImpreciseTransformationImpl(Class destinationClass, Class sourceClass) {
75 List conversionPath = getConversionPath(destinationClass, sourceClass);
76 return !isPrecise(conversionPath, sourceClass, 0);
77 }
78
79 /**
80 * Get the converter used when using a ChainedTransformer as a Copier.
81 * @return
82 */
83 protected synchronized Converter getCopyConverter() {
84 if (copyConverter == null) {
85 Transformer[] chain = getChain();
86 Assert.notNull(chain, "components");
87 if (chain.length == 2) {
88 copyConverter = getConverter(chain[0]);
89 } else {
90 Transformer[] newChain = new Transformer[chain.length - 1];
91 System.arraycopy(chain, 0, newChain, 0, newChain.length);
92 copyConverter = new ChainedTransformer(newChain);
93 }
94 }
95 return copyConverter;
96 }
97
98
99 private Converter getConverter(Transformer t) {
100 if (t instanceof Converter) {
101 return (Converter) t;
102 }
103 if (t instanceof Copier) {
104 return new CopierDecorator((Copier) t);
105 }
106 throw new IllegalArgumentException("Don't know how to use " + t + " as a Converter");
107 }
108
109 /**
110 * {@inheritDoc}
111 * @see net.sf.morph.transform.transformers.BaseTransformer#convertImpl(java.lang.Class, java.lang.Object, java.util.Locale)
112 */
113 protected Object convertImpl(Class destinationClass, Object source, Locale locale)
114 throws Exception {
115 if (log.isTraceEnabled()) {
116 log.trace("Using chain to convert "
117 + ObjectUtils.getObjectDescription(source) + " to "
118 + ObjectUtils.getObjectDescription(destinationClass));
119 }
120 Transformer[] chain = getChain();
121 Class sourceType = ClassUtils.getClass(source);
122 List conversionPath = getConversionPath(destinationClass, sourceType);
123 if (conversionPath == null) {
124 throw new TransformationException(destinationClass, sourceType, null,
125 "Chained conversion path could not be determined");
126 }
127 if (log.isDebugEnabled()) {
128 log.debug("Using chained conversion path " + conversionPath);
129 }
130 Object o = source;
131 for (int i = 0; i < conversionPath.size(); i++) {
132 o = getConverter(chain[i]).convert((Class) conversionPath.get(i), o, locale);
133 logConversion(i + 1, source, o);
134 }
135 return o;
136 }
137
138 /**
139 * {@inheritDoc}
140 * @see net.sf.morph.transform.transformers.BaseTransformer#copyImpl(java.lang.Object, java.lang.Object, java.util.Locale, java.lang.Integer)
141 */
142 protected void copyImpl(Object destination, Object source, Locale locale, Integer preferredTransformationType) throws Exception {
143 if (log.isTraceEnabled()) {
144 log.trace("Using chain to copy "
145 + ObjectUtils.getObjectDescription(source) + " to "
146 + ObjectUtils.getObjectDescription(destination));
147 }
148 Class destinationClass = ClassUtils.getClass(destination);
149 Transformer[] chain = getChain();
150 Transformer copier = chain[chain.length - 1];
151 if (!(copier instanceof Copier)) {
152 throw new TransformationException(destinationClass, source, null,
153 "Last chain component must be a Copier");
154 }
155 Class sourceType = ClassUtils.getClass(source);
156 List conversionPath = getConversionPath(destinationClass, sourceType);
157 if (conversionPath == null) {
158 throw new TransformationException(destinationClass, source, null,
159 "Chained conversion path could not be determined");
160 }
161 if (log.isDebugEnabled()) {
162 log.debug("Using chained conversion path " + conversionPath);
163 }
164 Object last = getCopyConverter().convert((Class) conversionPath.get(chain.length - 2), source, locale);
165 ((Copier) copier).copy(destination, last, locale);
166 }
167
168 /**
169 * Log one conversion in the chain.
170 * @param conversionNumber
171 * @param source
172 * @param destination
173 */
174 protected void logConversion(int conversionNumber, Object source, Object destination) {
175 if (log.isTraceEnabled()) {
176 log.trace("Conversion "
177 + conversionNumber
178 + " of "
179 + getComponents().length
180 + " was from "
181 + ObjectUtils.getObjectDescription(source)
182 + " to "
183 + ObjectUtils.getObjectDescription(destination)
184 + " and was performed by "
185 + ObjectUtils.getObjectDescription(getComponents()[conversionNumber - 1]));
186 }
187 }
188
189 /**
190 * {@inheritDoc}
191 * @see net.sf.morph.transform.transformers.BaseTransformer#getDestinationClassesImpl()
192 */
193 protected Class[] getDestinationClassesImpl() throws Exception {
194 return getChain()[getChain().length - 1].getDestinationClasses();
195 }
196
197 /**
198 * {@inheritDoc}
199 * @see net.sf.morph.transform.transformers.BaseTransformer#getSourceClassesImpl()
200 */
201 protected Class[] getSourceClassesImpl() throws Exception {
202 return getChain()[0].getSourceClasses();
203 }
204
205 /**
206 * Get the List of destination classes on the conversion path.
207 * @param destinationType
208 * @param sourceType
209 * @return List
210 */
211 protected List getConversionPath(Class destinationType, Class sourceType) {
212 return getConversionPath(destinationType, sourceType, 0);
213 }
214
215 /**
216 * Get a conversion path by investigating possibilities recursively.
217 * @param destinationType
218 * @param sourceType should be non-null if !allowNull
219 * @param chain
220 * @param index
221 * @param allowNull
222 * @return List
223 */
224 private List getConversionPath(Class destinationType, Class sourceType, int index) {
225 Transformer[] chain = getChain();
226 Transformer c = chain[index];
227 if (index + 1 == chain.length) {
228 if (TransformerUtils.isTransformable(c, destinationType, sourceType)) {
229 List result = new ArrayList();
230 result.add(destinationType);
231 return result;
232 }
233 return null;
234 }
235 List possibleResult = null;
236 Class[] available = c.getDestinationClasses();
237 for (int i = 0; i < available.length; i++) {
238 if (TransformerUtils.isTransformable(c, available[i], sourceType)) {
239 List tail = getConversionPath(destinationType, available[i], index + 1);
240 if (tail != null) {
241 tail.add(0, available[i]);
242 if (isPrecise(tail, sourceType, index)) {
243 return tail;
244 }
245 possibleResult = tail;
246 }
247 }
248 }
249 return possibleResult;
250 }
251
252 private boolean isPrecise(List conversionPath, Class sourceType, int index) {
253 Transformer[] chain = getChain();
254 Class currentSource = sourceType;
255 int i = 0;
256 for (Iterator iter = conversionPath.iterator(); iter.hasNext(); i++) {
257 Class currentDest = (Class) iter.next();
258 if (TransformerUtils.isImpreciseTransformation(chain[index + i], currentDest,
259 currentSource)) {
260 return false;
261 }
262 currentSource = currentDest;
263 }
264 return true;
265 }
266
267 /**
268 * Get the components array narrowed to a Transformer[].
269 * @return Transformer[]
270 */
271 protected synchronized Transformer[] getChain() {
272 return (Transformer[]) getComponents();
273 }
274
275 }