1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.collections15.functors.transformer;
17
18 import net.sf.collections15.Transformer;
19
20 import java.io.Serializable;
21
22 /***
23 * <code>Transformer</code> implementation that chains two specified
24 * <code>Transformer</code>s together. The principal restriction on the chaining
25 * together of two <code>Transformer</code>s into a chain is that the generic
26 * output type of the first <code>Transformer</code> must match the generic
27 * input type of the second <code>Transformer</code>. For example:
28 * <pre>
29 * Transformer<A, B> t1;
30 * Transformer<B, C> t2;
31 * ...
32 * Transformer<A, C> chained = ChainedTransformer.getInstance(t1, t2);
33 * </pre>
34 * The <code>transform()</code> method of a <code>ChainedTransformer</code>
35 * works by transforming an input object using the first
36 * <code>Transformer</code>, resulting in an intermediate object. This is then
37 * transformed by the second <code>Transformer</code>, resulting in the returned
38 * output object.
39 * <p/>
40 * The methods {@link #prepend} and {@link #append} can be used to add
41 * additional <code>Transformer</code>s to the chain, allowing multiple
42 * <code>Transformer</code>s to be chained together in a typesafe manner. In
43 * this way, a tree of <code>Transformers</code> can be built up, whereby the
44 * leaves of the tree form a typesafe chain of <code>Transformer</code>s The
45 * input type of the first leaf <code>Transformer</code> is <code>I</code>, and
46 * the output type of the last leaf <code>Transformer</code> is <code>O</code>.
47 * The transform process takes an input object, transforms it using the first
48 * leaf <code>Transformer</code>, and passes the result on to the next leaf.
49 * This process repeats along the chain of leaves, until the last leaf
50 * <code>Transformer</code> in the chain produces the final output object.
51 * <p/>
52 * Here's an example, showing the use of the static factory method, and both the
53 * <code>prepend</code> and <code>append</code> methods:
54 * <pre>
55 * Transformer<A, B> t1;
56 * Transformer<B, C> t2;
57 * Transformer<C, D> t3;
58 * Transformer<D, E> t4;
59 * ...
60 * Transformer<A, E> chained = ChainedTransformer.getInstance(t2, t3)
61 * .append(t4).prepend(t1);
62 *
63 * @author Chris Lambrou
64 * @since Collections15 1.0
65 */
66 public class ChainedTransformer <I, M, O> implements Transformer<I, O>, Serializable
67 {
68
69 static final long serialVersionUID = -8498145290061843155L;
70
71 /***
72 * The first <code>Transformer</code> that transforms the input object to
73 * the intermediate object.
74 */
75 private Transformer<? super I, ? extends M> firstTransformer;
76
77 /***
78 * The second <code>Transformer</code> that transforms the intermediate
79 * object to the output object.
80 */
81 private Transformer<? super M, ? extends O> secondTransformer;
82
83 /***
84 * Returns a new <code>ChainedTransformer</code> instance that chains
85 * together the two specified <code>Transformer</code>s.
86 *
87 * @param firstTransformer The first <code>Transformer</code> in the
88 * chain.
89 * @param secondTransformer The second <code>Transformer</code> in the
90 * chain.
91 *
92 * @return A new <code>ChainedTransformer</code> instance that chains
93 * together the two specified <code>Transformer</code>s.
94 *
95 * @throws IllegalArgumentException Thrown if either <code>Transformer</code>
96 * is <code>null</code>.
97 */
98 public static <I, M, O> ChainedTransformer<I, M, O> getInstance(Transformer<? super I, ? extends M> firstTransformer,
99 Transformer<? super M, ? extends O> secondTransformer)
100 {
101 return new ChainedTransformer<I, M, O>(firstTransformer, secondTransformer);
102 }
103
104 /***
105 * Creates a new instance that chains together the two specified
106 * <code>Transformer</code>s.
107 *
108 * @param firstTransformer The first <code>Transformer</code> in the
109 * chain.
110 * @param secondTransformer The second <code>Transformer</code> in the
111 * chain.
112 *
113 * @throws IllegalArgumentException Thrown if either <code>Transformer</code>
114 * is <code>null</code>.
115 */
116 protected ChainedTransformer(Transformer<? super I, ? extends M> firstTransformer,
117 Transformer<? super M, ? extends O> secondTransformer)
118 {
119 if (firstTransformer == null) {
120 throw new IllegalArgumentException("null first transformer");
121 }
122 if (secondTransformer == null) {
123 throw new IllegalArgumentException("null second transformer");
124 }
125 this.firstTransformer = firstTransformer;
126 this.secondTransformer = secondTransformer;
127 }
128
129 /***
130 * Appends a specified <code>Transformer</code> to the end of the chain,
131 * resulting in a new <code>ChainedTransformer</code> that transforms an
132 * input object through the existing <code>Transformer</code>s in this
133 * chain, and then transforms the result through the specified
134 * <code>Transformer</code>.
135 *
136 * @param transformer The <code>Transformer</code> to append to the chain.
137 *
138 * @return A new <code>ChainedTransformer</code> that transforms an input
139 * object through the existing <code>Transformer</code>s in this
140 * chain, and then transforms the result through the specified
141 * <code>Transformer</code>.
142 *
143 * @throws IllegalArgumentException Thrown if the specified <code>Transformer</code>
144 * is <code>null</code>.
145 */
146 public <T> ChainedTransformer<I, O, T> append(Transformer<? super O, T> transformer)
147 {
148 return new ChainedTransformer<I, O, T>(this, transformer);
149 }
150
151 /***
152 * Prepends a specified <code>Transformer</code> to the start of the chain,
153 * resulting in a new <code>ChainedTransformer</code> that transforms an
154 * input object through the specified <code>Transformer</code>, and then
155 * transforms the result through the existing <code>Transformer</code>s in
156 * this chain.
157 *
158 * @param transformer The <code>Transformer</code> to prepend to the chain.
159 *
160 * @return A new <code>ChainedTransformer</code> that transforms an input
161 * object through the specified <code>Transformer</code>, and then
162 * transforms the result through the existing <code>Transformer</code>s
163 * in this chain.
164 *
165 * @throws IllegalArgumentException Thrown if the specified <code>Transformer</code>
166 * is <code>null</code>.
167 */
168 public <T> ChainedTransformer<T, I, O> prepend(Transformer<T, ? extends I> transformer)
169 {
170 return new ChainedTransformer<T, I, O>(transformer, this);
171 }
172
173 /***
174 * Transforms the input object by transforming it using the first
175 * <code>Transformer</code>, and then transforming the result using the
176 * second <code>Transformer</code>, returning the resulting output object.
177 *
178 * @param input The object to be transformed.
179 *
180 * @return The transformed object
181 */
182 public O transform(I input)
183 {
184 return secondTransformer.transform(firstTransformer.transform(input));
185 }
186
187 }