1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.morph.util;
17
18 import java.util.Arrays;
19 import java.util.Iterator;
20 import java.util.Locale;
21 import java.util.Map;
22 import java.util.Set;
23
24 import net.sf.composite.util.ObjectUtils;
25 import net.sf.morph.transform.Converter;
26 import net.sf.morph.transform.Copier;
27 import net.sf.morph.transform.ExplicitTransformer;
28 import net.sf.morph.transform.ImpreciseTransformer;
29 import net.sf.morph.transform.TransformationException;
30 import net.sf.morph.transform.Transformer;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34
35 /**
36 * Utility functions for implementing and using Transformers.
37 *
38 * @author Matt Sgarlata
39 * @since Nov 26, 2004
40 */
41 public abstract class TransformerUtils {
42
43 private static abstract class ClassStrategy {
44 abstract Class[] get(Transformer t);
45 }
46
47 private static final ClassStrategy SOURCE = new ClassStrategy() {
48 Class[] get(Transformer t) {
49 return t.getSourceClasses();
50 }
51 };
52
53 private static final ClassStrategy DEST = new ClassStrategy() {
54 Class[] get(Transformer t) {
55 return t.getDestinationClasses();
56 }
57 };
58
59 private static final Log log = LogFactory.getLog(TransformerUtils.class);
60 private static final Class[] CLASS_NONE = new Class[0];
61
62 /**
63 * Learn whether <code>sourceClass</code> is transformable to <code>destinationClass</code>
64 * by <code>transformer</code> considering only source and destination types.
65 * @param transformer
66 * @param destinationClass
67 * @param sourceClass
68 * @return boolean
69 */
70 public static boolean isImplicitlyTransformable(Transformer transformer, Class destinationClass, Class sourceClass) {
71 return ClassUtils.inheritanceContains(transformer.getDestinationClasses(), destinationClass)
72 && ClassUtils.inheritanceContains(transformer.getSourceClasses(), sourceClass);
73 }
74
75 /**
76 * Learn whether <code>sourceClass</code> is transformable to <code>destinationClass</code>
77 * by <code>transformer</code> by implicit or explicit rules.
78 * @param transformer
79 * @param destinationClass
80 * @param sourceClass
81 * @return boolean
82 * @see #isImplicitlyTransformable(Transformer, Class, Class)
83 * @see ExplicitTransformer
84 */
85 public static boolean isTransformable(Transformer transformer, Class destinationClass, Class sourceClass) {
86 if (transformer instanceof ExplicitTransformer) {
87 return ((ExplicitTransformer) transformer).isTransformable(destinationClass, sourceClass);
88 }
89 return isImplicitlyTransformable(transformer, destinationClass, sourceClass);
90 }
91
92 /**
93 * Learn whether <code>transformer</code>'s transformation
94 * of <code>sourceClass</code> to <code>destinationClass</code> might yield an imprecise result.
95 * @param transformer
96 * @param destinationClass
97 * @param sourceClass
98 * @return boolean
99 * @see ImpreciseTransformer
100 * @since Morph 1.1
101 */
102 public static boolean isImpreciseTransformation(Transformer transformer,
103 Class destinationClass, Class sourceClass) {
104 if (transformer instanceof ImpreciseTransformer) {
105 return ((ImpreciseTransformer) transformer).isImpreciseTransformation(
106 destinationClass, sourceClass);
107 }
108 return destinationClass == null && sourceClass != null;
109 }
110
111 /**
112 * Performs a transformation of one object graph into another object graph.
113 *
114 * @param destinationType
115 * the type of the root node of the destination object graph
116 * @param the
117 * optional root node of the destination object graph. If this
118 * parameter is specified, it will be possible to copy
119 * information to an existing object graph rather than requiring
120 * a new object graph be created
121 * @param source
122 * the root node of the source object graph
123 * @param locale
124 * the locale in which any needed transformations should take
125 * place
126 * @param preferredTransformationType
127 * the preferred type of transformation to be performed
128 * @return the transformed object graph
129 * @throws TransformationException
130 * if the graph could not be transformed for some reason
131 * @see Converter#TRANSFORMATION_TYPE_CONVERT
132 * @see Copier#TRANSFORMATION_TYPE_COPY
133 */
134 public static Object transform(Transformer transformer, Class destinationType, Object destination,
135 Object source, Locale locale, Integer preferredTransformationType)
136 throws TransformationException {
137
138
139 Integer xform = preferredTransformationType != null ? preferredTransformationType
140 : transformer instanceof Copier ? Transformer.TRANSFORMATION_TYPE_COPY
141 : Transformer.TRANSFORMATION_TYPE_CONVERT;
142
143 boolean mutableDest = !ClassUtils.isImmutableObject(destination);
144
145
146
147
148 if (Transformer.TRANSFORMATION_TYPE_COPY.equals(xform)) {
149 if (transformer instanceof Converter && !mutableDest) {
150 xform = Transformer.TRANSFORMATION_TYPE_CONVERT;
151 }
152 }
153 else if (Transformer.TRANSFORMATION_TYPE_CONVERT.equals(xform)) {
154 if (!(transformer instanceof Converter) && transformer instanceof Copier
155 && mutableDest) {
156 xform = Transformer.TRANSFORMATION_TYPE_COPY;
157 }
158 }
159
160 if (Transformer.TRANSFORMATION_TYPE_CONVERT.equals(xform)) {
161 if (log.isTraceEnabled()) {
162 log.trace("Performing nested conversion of "
163 + ObjectUtils.getObjectDescription(source)
164 + " to destination type "
165 + ObjectUtils.getObjectDescription(destinationType));
166 }
167 try {
168 return ((Converter) transformer).convert(destinationType, source, locale);
169 }
170 catch (TransformationException e) {
171 throw e;
172 }
173 catch (Exception e) {
174 throw new TransformationException("Unable to perform transformation", e);
175 }
176 }
177 if (Transformer.TRANSFORMATION_TYPE_COPY.equals(xform)) {
178 if (log.isTraceEnabled()) {
179 log.trace("Performing nested copy of "
180 + ObjectUtils.getObjectDescription(source)
181 + " to destination "
182 + ObjectUtils.getObjectDescription(destination));
183 }
184 try {
185 ((Copier) transformer).copy(destination, source, locale);
186 }
187 catch (TransformationException e) {
188 throw e;
189 }
190 catch (Exception e) {
191 throw new TransformationException("Unable to perform graph transformation", e);
192 }
193 return destination;
194 }
195
196
197 throw new TransformationException(
198 "Unable to perform transformation using transformer "
199 + ObjectUtils.getObjectDescription(transformer));
200 }
201
202 /**
203 * Get the mapped destination type from the specified typemap.
204 * @param typeMapping Map
205 * @param requestedType Class
206 * @return Class
207 */
208 public static Class getMappedDestinationType(Map typeMapping, Class requestedType) {
209 if (typeMapping == null) {
210 return null;
211 }
212
213 Class mappedDestinationType = (Class) typeMapping.get(requestedType);
214
215 if (mappedDestinationType == null) {
216 Set keys = typeMapping.keySet();
217 for (Iterator i = keys.iterator(); i.hasNext();) {
218 Class type = (Class) i.next();
219 if (type.isAssignableFrom(requestedType)) {
220 mappedDestinationType = (Class) typeMapping.get(type);
221 break;
222 }
223 }
224 }
225 return mappedDestinationType;
226 }
227
228 /**
229 * Get the set of source classes available from the specified Transformer for the specified destination type.
230 * @param transformer
231 * @param destinationType
232 * @return Class[]
233 */
234 public static Class[] getSourceClasses(Transformer transformer, Class destinationType) {
235 if (!ClassUtils.inheritanceContains(transformer.getDestinationClasses(), destinationType)) {
236 return CLASS_NONE;
237 }
238 Class[] sourceTypes = transformer.getSourceClasses();
239 if (transformer instanceof ExplicitTransformer) {
240 Set result = ContainerUtils.createOrderedSet();
241 for (int i = 0; i < sourceTypes.length; i++) {
242 if (((ExplicitTransformer) transformer).isTransformable(destinationType, sourceTypes[i])) {
243 result.add(sourceTypes[i]);
244 }
245 }
246 return result.isEmpty() ? CLASS_NONE : (Class[]) result.toArray(new Class[result.size()]);
247 }
248 return sourceTypes;
249 }
250
251 /**
252 * Get the set of destination classes available from the specified Transformer for the specified source type.
253 * @param transformer
254 * @param sourceType
255 * @return Class[]
256 * @since Morph 1.1
257 */
258 public static Class[] getDestinationClasses(Transformer transformer, Class sourceType) {
259 if (!ClassUtils.inheritanceContains(transformer.getSourceClasses(), sourceType)) {
260 return CLASS_NONE;
261 }
262 Class[] destinationTypes = transformer.getDestinationClasses();
263 if (transformer instanceof ExplicitTransformer) {
264 Set result = ContainerUtils.createOrderedSet();
265 for (int i = 0; i < destinationTypes.length; i++) {
266 if (((ExplicitTransformer) transformer).isTransformable(destinationTypes[i], sourceType)) {
267 result.add(destinationTypes[i]);
268 }
269 }
270 return result.isEmpty() ? CLASS_NONE : (Class[]) result.toArray(new Class[result.size()]);
271 }
272 return destinationTypes;
273 }
274
275 /**
276 * Get the set of source classes common to all specified Transformers.
277 * @param transformers
278 * @return Class[]
279 */
280 public static Class[] getSourceClassIntersection(Transformer[] transformers) {
281 return getClassIntersection(transformers, SOURCE);
282 }
283
284 /**
285 * Get the set of destination classes common to all specified Transformers.
286 * @param transformers
287 * @return Class[]
288 */
289 public static Class[] getDestinationClassIntersection(Transformer[] transformers) {
290 return getClassIntersection(transformers, DEST);
291
292 }
293
294 private static Class[] getClassIntersection(Transformer[] transformers, ClassStrategy strategy) {
295 Set s = ContainerUtils.createOrderedSet();
296 s.addAll(Arrays.asList(strategy.get(transformers[0])));
297
298 for (int i = 1; i < transformers.length; i++) {
299 Set survivors = ContainerUtils.createOrderedSet();
300 Class[] c = strategy.get(transformers[i]);
301 for (int j = 0; j < c.length; j++) {
302 if (s.contains(c[j])) {
303 survivors.add(c[j]);
304 break;
305 }
306 if (c[j] == null) {
307 break;
308 }
309 for (Iterator it = s.iterator(); it.hasNext();) {
310 Class next = (Class) it.next();
311 if (next != null && next.isAssignableFrom(c[j])) {
312 survivors.add(c[j]);
313 break;
314 }
315 }
316 }
317 if (!survivors.containsAll(s)) {
318 for (Iterator it = s.iterator(); it.hasNext();) {
319 Class next = (Class) it.next();
320 if (survivors.contains(next) || next == null) {
321 break;
322 }
323 for (int j = 0; j < c.length; j++) {
324 if (c[j] != null && c[j].isAssignableFrom(next)) {
325 survivors.add(next);
326 break;
327 }
328 }
329 }
330 }
331 s = survivors;
332 }
333 return s.isEmpty() ? CLASS_NONE : (Class[]) s.toArray(new Class[s.size()]);
334 }
335
336 }