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.MultplePredicateDecorator;
19 import net.sf.collections15.Predicate;
20 import net.sf.collections15.Transformer;
21
22 import java.io.Serializable;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Map;
27
28 /***
29 * <code>Transformer</code> implementation that defers to the
30 * <code>Transformer</code> whose corresponding <code>Predicate</code> evaluates
31 * to <code>true</code>, like a switch statement.
32 *
33 * @author Chris Lambrou
34 * @since Collections15 1.0
35 */
36 public class SwitchTransformer <I, O> implements Transformer<I, O>, MultplePredicateDecorator<I>, Serializable
37 {
38
39 static final long serialVersionUID = 1208341652199297260L;
40
41 /***
42 * The <code>Predicate</code> that define the tests to evaluate.
43 */
44 private List<Predicate<? super I>> predicates;
45
46 /***
47 * The <code>Transformer</code>s that implement each case.
48 */
49 private List<Transformer<? super I, ? extends O>> transformers;
50
51 /***
52 * The default <code>Transformer</code> to use if no tests match. May be
53 * <code>null</code>, in which case an <code>IllegalArgumentException</code>
54 * is thrown if this <code>Transformer</code> attempts to transform an input
55 * object which doesn't satisfy any of the cases.
56 */
57 private Transformer<? super I, ? extends O> defaultTransformer;
58
59 /***
60 * Creates a new instance that uses the specified map of
61 * <code>Predicates</code> and <code>Transformer</code>s.
62 *
63 * @param predicatesAndTransformers A map of <code>Predicate</code>s to
64 * <code>Transformer</code>s. Each non-null
65 * <code>Predicate</code> key defines a
66 * single switch case, with the
67 * corresponding <code>Transformer</code>
68 * being used to transform an input object
69 * if it matches the <code>Predicate</code>.
70 * <p/>
71 * <code>Predicate</code>s are evaluated in
72 * the iteration order of the map's key
73 * set, with the exception of the
74 * <code>null Predicate</code> key. If a
75 * <code>null Predicate</code> key is
76 * specified in the set, it's corresponding
77 * <code>Transformer</code> is treated as
78 * the default case, used to transform an
79 * input object if it doesn't satisfy any
80 * of the non-<code>null Predicate</code>
81 * cases.
82 * <p/>
83 * None of the <code>Transformer</code>
84 * values may be <code>null</code>, and
85 * only one <code>Predicate</code> may be
86 * <code>null</code>.
87 * <p/>
88 * <b>Note that the map is not modified by
89 * this method</b>.
90 *
91 * @throws IllegalArgumentException Thrown if the map is <code>null</code>.
92 * @throws IllegalArgumentException Thrown if any <code>Transformer</code>
93 * in the map is <code>null</code>.
94 */
95 private SwitchTransformer(Map<Predicate<I>, Transformer<I, O>> predicatesAndTransformers)
96 {
97 if (predicatesAndTransformers == null) {
98 throw new IllegalArgumentException("The predicate and transformer map must not be null");
99 }
100
101 Transformer<I, O> defaultTransformer = null;
102 int size = predicatesAndTransformers.size();
103 predicates = new ArrayList<Predicate<? super I>>(size);
104 transformers = new ArrayList<Transformer<? super I, ? extends O>>(size);
105 for (Map.Entry<? extends Predicate<I>, ? extends Transformer<I, O>> entry : predicatesAndTransformers.entrySet()) {
106 Predicate<I> predicate = entry.getKey();
107 Transformer<I, O> transformer = entry.getValue();
108 if (transformer == null) {
109 throw new NullPointerException("null transformer specified");
110 }
111 if (predicate == null) {
112 if (defaultTransformer == null) {
113 defaultTransformer = transformer;
114 continue;
115 }
116 else {
117 throw new NullPointerException("multiple null predicates specified");
118 }
119 }
120 predicates.add(predicate);
121 transformers.add(transformer);
122 }
123 this.defaultTransformer = defaultTransformer;
124 }
125
126 /***
127 * Creates a new instance that uses the specified map of
128 * <code>Predicates</code> and <code>Transformer</code>s.
129 *
130 * @param predicatesAndTransformers A map of <code>Predicate</code>s to
131 * <code>Transformer</code>s. Each non-null
132 * <code>Predicate</code> key defines a
133 * single switch case, with the
134 * corresponding <code>Transformer</code>
135 * being used to transform an input object
136 * if it matches the <code>Predicate</code>.
137 * <p/>
138 * <code>Predicate</code>s are evaluated in
139 * the iteration order of the map's key
140 * set, with the exception of the
141 * <code>null Predicate</code> key. If a
142 * <code>null Predicate</code> key is
143 * specified in the set, it's corresponding
144 * <code>Transformer</code> is treated as
145 * the default case, used to transform an
146 * input object if it doesn't satisfy any
147 * of the non-<code>null Predicate</code>
148 * cases.
149 * <p/>
150 * None of the <code>Transformer</code>
151 * values may be <code>null</code>, and
152 * only one <code>Predicate</code> may be
153 * <code>null</code>.
154 * <p/>
155 * <b>Note that the map is not modified by
156 * this method</b>.
157 *
158 * @return A new instance that uses the specified map of
159 * <code>Predicates</code> and <code>Transformer</code>s.
160 *
161 * @throws IllegalArgumentException Thrown if the map is <code>null</code>.
162 * @throws IllegalArgumentException Thrown if any <code>Transformer</code>
163 * in the map is <code>null</code>.
164 */
165 public static <I, O> Transformer<I, O> getInstance(Map<Predicate<I>, Transformer<I, O>> predicatesAndTransformers)
166 {
167 if (predicatesAndTransformers == null) {
168 throw new IllegalArgumentException("The predicate and transformer map must not be null");
169 }
170 int size = predicatesAndTransformers.size();
171 if (size == 1 && predicatesAndTransformers.containsKey(null)) {
172 Transformer<I, O> defaultTransformer = predicatesAndTransformers.get(null);
173 if (defaultTransformer == null) {
174 throw new IllegalArgumentException("null default transformer specified");
175 }
176 return defaultTransformer;
177 }
178 return new SwitchTransformer<I, O>(predicatesAndTransformers);
179 }
180
181 /***
182 * Transforms the input to result by delegating to the
183 * <code>Transformer</code> whose matching <code>Predicate</code> evaluates
184 * to <code>true</code>. If no <code>Predicate</code> evaluates to
185 * <code>true</code>, the default <code>Transformer</code> is used. If there
186 * is no default <code>Transformer</code>, an <code>IllegalArgumentException</code>
187 * is thrown.
188 *
189 * @param input The input object to transform.
190 *
191 * @return The transformed result
192 *
193 * @throws IllegalArgumentException Thrown if the input doesn't match any of
194 * the cases, and no default <code>Transformer</code>
195 * is defined.
196 */
197 public O transform(I input)
198 {
199 int size = predicates.size();
200 for (int i = 0; i < size; i++) {
201 Predicate<? super I> predicate = predicates.get(i);
202 if (predicate.evaluate(input)) {
203 return transformers.get(i).transform(input);
204 }
205 }
206 if (defaultTransformer == null) {
207 throw new IllegalArgumentException("input doesn't match any cases, and no default is defined");
208 }
209 return defaultTransformer.transform(input);
210 }
211
212 /***
213 * Returns a list of the <code>Predicate</code>s that define each case.
214 *
215 * @return A list of the decorated <code>Predicate</code>s that define each
216 * case.. The returned list is unmodifiable.
217 */
218 public List<Predicate<? super I>> getDecoratedPredicates()
219 {
220 return Collections.unmodifiableList(predicates);
221 }
222
223 /***
224 * Returns the <code>Transformer</code>s used by each case.
225 *
226 * @return A list of <code>Transformer</code>s used by this
227 * <code>SwitchTransformer</code>. There is a one to one mapping
228 * between the <code>Transformer</code>s in the list and the
229 * <code>Predicate</code>s in the list returned by {@link
230 * #getDecoratedPredicates}.
231 *
232 * @see #getDefaultTransformer().
233 */
234 public List<Transformer<? super I, ? extends O>> getTransformers()
235 {
236 return Collections.unmodifiableList(transformers);
237 }
238
239 /***
240 * Returns the <code>Transformer</code> used in the default case.
241 *
242 * @return The <code>Transformer</code> used to transform an input object if
243 * it doesn't match any of the <code>Predicate</code>s returned by
244 * {@link #getDecoratedPredicates} - <code>null</code> if no default
245 * is defined.
246 *
247 * @see #getDecoratedPredicates()
248 * @see #getTransformers()
249 * @since Collections15 1.0
250 */
251 public Transformer<? super I, ? extends O> getDefaultTransformer()
252 {
253 return defaultTransformer;
254 }
255 }