1 | package org.springframework.batch.item.file.transform; |
2 | |
3 | import java.util.Collection; |
4 | import java.util.Iterator; |
5 | |
6 | import org.springframework.batch.item.transform.ItemTransformer; |
7 | |
8 | /** |
9 | * An implementation of {@link ItemTransformer} that treats its argument |
10 | * specially if it is an array or collection. In this case it loops though, |
11 | * calling itself on each member in turn, until it encounters a non collection. |
12 | * At this point, if the item is a String, that is used, or else it is passed to |
13 | * the delegate {@link ItemTransformer}. The transformed single item Strings |
14 | * are all concatenated with line separators. |
15 | * |
16 | * @author Dave Syer |
17 | * |
18 | */ |
19 | public class RecursiveCollectionItemTransformer implements ItemTransformer { |
20 | |
21 | private static final String LINE_SEPARATOR = System.getProperty("line.separator"); |
22 | |
23 | private ItemTransformer delegate = new ItemTransformer() { |
24 | public Object transform(Object item) throws Exception { |
25 | return item; |
26 | } |
27 | }; |
28 | |
29 | /** |
30 | * Public setter for the {@link ItemTransformer} to use on single items, |
31 | * that are not Strings. This can be used to strategise the conversion of |
32 | * collection and array elements to a String, e.g. via a subclass of |
33 | * {@link LineAggregatorItemTransformer}.<br/> |
34 | * |
35 | * N.B. if the delegate returns an array or collection, it will not be |
36 | * treated the same way as the original item passed in for transformation. |
37 | * Rather, in this case, it will simply be converted immediately to a String |
38 | * by calling its toString(). |
39 | * |
40 | * @param delegate the delegate to set. Defaults to a pass through. |
41 | */ |
42 | public void setDelegate(ItemTransformer delegate) { |
43 | this.delegate = delegate; |
44 | } |
45 | |
46 | /* |
47 | * (non-Javadoc) |
48 | * @see org.springframework.batch.item.writer.ItemTransformer#transform(java.lang.Object) |
49 | */ |
50 | public Object transform(Object input) throws Exception { |
51 | TransformHolder holder = new TransformHolder(); |
52 | transformRecursively(input, holder); |
53 | String result = holder.builder.toString(); |
54 | return result.substring(0, result.lastIndexOf(LINE_SEPARATOR)); |
55 | } |
56 | |
57 | public String stringify(Object item) throws Exception { |
58 | return "" + delegate.transform(item); |
59 | } |
60 | |
61 | /** |
62 | * Convert the date to a format that can be output and then write it out. |
63 | * @param data |
64 | * @param converted |
65 | * @throws Exception |
66 | */ |
67 | private void transformRecursively(Object data, TransformHolder converted) throws Exception { |
68 | |
69 | if (data instanceof Collection) { |
70 | for (Iterator iterator = ((Collection) data).iterator(); iterator.hasNext();) { |
71 | Object value = (Object) iterator.next(); |
72 | // (recursive) |
73 | transformRecursively(value, new TransformHolder(converted.builder)); |
74 | } |
75 | return; |
76 | } |
77 | if (data.getClass().isArray()) { |
78 | Object[] array = (Object[]) data; |
79 | for (int i = 0; i < array.length; i++) { |
80 | Object value = array[i]; |
81 | // (recursive) |
82 | transformRecursively(value, new TransformHolder(converted.builder)); |
83 | } |
84 | return; |
85 | } |
86 | if (data instanceof String) { |
87 | // This is where the output stream is actually written to |
88 | converted.builder.append(data + LINE_SEPARATOR); |
89 | } |
90 | else { |
91 | // (recursive) |
92 | transformRecursively(stringify(data), converted); |
93 | return; |
94 | } |
95 | } |
96 | |
97 | private static class TransformHolder { |
98 | |
99 | StringBuffer builder = new StringBuffer(); |
100 | |
101 | TransformHolder() { |
102 | } |
103 | |
104 | TransformHolder(StringBuffer builder) { |
105 | this.builder = builder; |
106 | } |
107 | } |
108 | } |