1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.collections15.functors.closure;
17
18 import net.sf.collections15.Closure;
19 import net.sf.collections15.Predicate;
20 import net.sf.collections15.Transformer;
21 import net.sf.collections15.functors.transformer.InvokerTransformer;
22
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Map;
26
27 /***
28 * <code>ClosureUtils</code> provides reference implementations and utilities
29 * for the <code>Closure</code> functor interface. The supplied
30 * <code>Closures</code> are: <ul> <li>Invoker - invokes a method on the input
31 * object.</li> <li>If - conditionally executes a <code>Closure</code> if a
32 * <code>Predicate</code> holds <code>true</code>.</li> <li>IfElse -
33 * conditionally executes one of two <code>Closure</code>s depending on the
34 * outcome of a <code>Predicate</code>.</li> <li>For - repeatedly calls a
35 * <code>Closure</code> for a fixed number of times.</li> <li>While - repeatedly
36 * calls a <code>Closure</code> while a </code>Predicate</code> holds
37 * <code>true</code>.</li> <li>DoWhile - repeatedly calls a <code>Closure</code>
38 * while a <code>Predicate</code> holds <code>true</code>/</li> <li>Chained -
39 * chains two or more <code>Closures</code> together.</li> <li>Switch - calls
40 * one <code>Closure</code> based on one or more <code>Predicates</code>.</li>
41 * <li>SwitchMap - calls one <code>Closure</code> looked up from a map.</li>
42 * <li>Transformer - wraps a <code>Transformer</code> as a
43 * <code>Closure</code>.</li> <li>NOP - does nothing.</li> <li>Exception -
44 * always throws an exception.</li> </ul> All the supplied <code>Closures</code>
45 * are <code>Serializable</code>.
46 *
47 * @author Stephen Colebourne
48 * @author Chris Lambrou (port to Java 5.0)
49 * @since Collections15 1.0
50 */
51 public class ClosureUtils
52 {
53
54 /***
55 * Protected constructor prevents direct instantiation, but allows users to
56 * extend this library to provide their own augmented static library class.
57 */
58 protected ClosureUtils()
59 {
60 }
61
62 /***
63 * Gets a <code>Closure</code> that always throws an exception. This could
64 * be useful during testing as a placeholder.
65 *
66 * @return A <code>Closure</code> whose {@link Closure#execute execute}
67 * method always throws an exception.
68 * @see ExceptionClosure
69 */
70 public static <T> Closure<T> exceptionClosure()
71 {
72 return ExceptionClosure.getInstance();
73 }
74
75 /***
76 * Gets a <code>Closure</code> that will do nothing. This could be useful
77 * during testing as a placeholder.
78 *
79 * @return A <code>Closure</code> whose {@link Closure#execute execute}
80 * method does nothing.
81 * @see NOPClosure
82 */
83 public static <T> Closure<T> nopClosure()
84 {
85 return NOPClosure.getInstance();
86 }
87
88 /***
89 * Creates a <code>Closure</code> that wraps a <code>Transformer</code>,
90 * invoking its {@link Transformer#transform transform} method each time it
91 * is executed. The <code>Transformer</code>'s output value is ignored.
92 *
93 * @param transformer The <code>Transformer</code> to wrap.
94 * @return A <code>Closure</code> whose {@link Closure#execute execute}
95 * method invokes the {@link Transformer#transform transform} method
96 * of the wrapped <codeTransformer</code>, ignoring the output of
97 * the transform.
98 * @throws IllegalArgumentException Thrown if the specified <code>Transformer</code>
99 * is <code>null</code>.
100 * @see TransformerClosure
101 */
102 public static <T> Closure<T> asClosure(Transformer<? super T, ?> transformer)
103 {
104 return TransformerClosure.getInstance(transformer);
105 }
106
107 /***
108 * Creates a decorator for an existing <code>Closure</code> that executes
109 * the specified <code>Closure</code> the specified number of times.
110 *
111 * @param count The number of times to execute the decorated
112 * <code>Closure</code>.
113 * @param closure The <code>Closure</code> to execute.
114 * @return A <code>Closure</code> whose {@link Closure#execute execute}
115 * method executes the <code>Closure</code> the specified number of
116 * times.
117 * @throws IllegalArgumentException Thrown if <code>count</code> < 0.
118 * @throws IllegalArgumentException Thrown if <code>closure</code> is
119 * <code>null</code>.
120 * @see ForClosure
121 */
122 public static <T> Closure<T> forClosure(int count, Closure<? super T> closure)
123 {
124 if (count < 0) {
125 throw new IllegalArgumentException("negative count specified");
126 }
127 if (closure == null) {
128 throw new IllegalArgumentException("null closure");
129 }
130 switch (count) {
131 case 0:
132 return NOPClosure.getInstance();
133 case 1:
134 return (Closure<T>) closure;
135 default:
136 return ForClosure.decorate(count, closure);
137 }
138 }
139
140 /***
141 * Returns a <code>Closure</code> instance that decorates the specified
142 * <code>Closure</code> using the specified <code>Predicate</code> condition
143 * to effect a <code>while</code> loop. The <code>Predicate</code> is
144 * evaluated at the start of the loop, and if it holds <code>false</code>
145 * the loop is terminated.
146 *
147 * @param predicate The <code>Predicate</code> used to determine when the
148 * loop should terminate.
149 * @param closure The <code>Closure</code> executed in the loop.
150 * @return A <code>Closure</code> whose {@link Closure#execute execute}
151 * method repeatedly executes the specified <code>Closure</code> as
152 * long as the <code>Predicate</code> holds <code>true</code>.
153 * @throws IllegalArgumentException Thrown if the <code>Predicate</code> or
154 * <code>Closure</code> is <code>null</code>.
155 * @see WhileClosure
156 */
157 public static <T> Closure<T> whileClosure(Predicate<? super T> predicate, Closure<? super T> closure)
158 {
159 return WhileClosure.<T>decorate(predicate, closure, false);
160 }
161
162 /***
163 * Returns a <code>Closure</code> instance that decorates the specified
164 * <code>Closure</code> using the specified <code>Predicate</code> condition
165 * to effect a <code>while</code> loop. The <code>Predicate</code> is
166 * evaluated at the end of the loop, and if it holds <code>false</code> the
167 * loop is terminated. This guarantees that the decorated
168 * <code>Closure</code> is executed at least once.
169 *
170 * @param predicate The <code>Predicate</code> used to determine when the
171 * loop should terminate.
172 * @param closure The <code>Closure</code> executed in the loop.
173 * @return A <code>Closure</code> whose {@link Closure#execute execute}
174 * method repeatedly executes the specified <code>Closure</code>
175 * until the <code>Predicate</code> holds <code>false</code>.
176 * @throws IllegalArgumentException Thrown if the <code>Predicate</code> or
177 * <code>Closure</code> is <code>null</code>.
178 * @see WhileClosure
179 */
180 public static <T> Closure<T> doWhileClosure(Predicate<? super T> predicate, Closure<? super T> closure)
181 {
182 return WhileClosure.<T>decorate(predicate, closure, true);
183 }
184
185
186 /***
187 * Creates a <code>Closure</code> that will invoke a specified method on the
188 * <code>Closure</code>'s input object by reflection.
189 *
190 * @param methodName The name of the method to execute on the target
191 * object.
192 * @return A <code>Closure</code> whose {@link Closure#execute execute}
193 * method invokes the named method on the input object.
194 * @throws IllegalArgumentException Thrown if the method name is null.
195 * @see InvokerTransformer
196 * @see TransformerClosure
197 */
198 public static <T> Closure<T> invokerClosure(String methodName)
199 {
200
201 Transformer<T, ?> transformer = InvokerTransformer.getInstance(methodName);
202 return asClosure(transformer);
203 }
204
205 /***
206 * Creates a <code>Closure</code> that will invoke a specified method on the
207 * <code>Closure</code>'s input object by reflection, with the specified
208 * arguments.
209 *
210 * @param methodName The name of the method to execute on the target
211 * object.
212 * @param paramTypes The parameter types of the invoked method.
213 * @param args The arguments passed to the invoked method.
214 * @return A <code>Closure</code> whose {@link Closure#execute execute}
215 * method invokes the named method on the input object.
216 * @throws IllegalArgumentException Thrown if the method name is null.
217 * @throws IllegalArgumentException Thrown if parameter types and argument
218 * arrays are <code>null</code>, or do not
219 * otherwise match.
220 * @see InvokerTransformer
221 * @see TransformerClosure
222 */
223 public static <T> Closure<T> invokerClosure(String methodName, Class[] paramTypes, Object[] args)
224 {
225
226 Transformer<T, ?> transformer = InvokerTransformer.getInstance(methodName, paramTypes, args);
227 return asClosure(transformer);
228 }
229
230 /***
231 * Create a new <code>Closure</code> that decorates a collection of existing
232 * <code>Closure</code>s. Each <code>Closure</code> is executed in turn on
233 * an input object, in the order that the <code>Closure</code>s are returned
234 * by the collection's iterator.
235 *
236 * @param closures A collection of <code>Closure</code>s to chain.
237 * @return A <code>Closure</code> whose {@link Closure#execute execute}
238 * method executes each of the decorated <code>Closure</code>s in
239 * turn.
240 * @throws IllegalArgumentException Thrown if the collection of
241 * <code>Closure</code>s is <code>null</code>
242 * or contains any <code>null</code>
243 * elements.
244 * @see ChainedClosure
245 */
246 public static <T> Closure<T> chainedClosure(Collection<Closure<? super T>> closures)
247 {
248 if (closures == null) {
249 throw new IllegalArgumentException("null closures collection");
250 }
251 switch (closures.size()) {
252 case 0:
253 return NOPClosure.getInstance();
254 case 1:
255 Closure<T> closure = (Closure<T>) closures.iterator().next();
256 if (closure == null) {
257 throw new IllegalArgumentException("null closure in specified collection");
258 }
259 return closure;
260 default:
261 return ChainedClosure.getInstance(closures);
262 }
263 }
264
265 /***
266 * Create a new <code>Closure</code> that decorates a collection of existing
267 * <code>Closure</code>s. Each <code>Closure</code> is executed in turn on
268 * an input object, in the order that the <code>Closure</code>s are returned
269 * by the collection's iterator.
270 *
271 * @param firstClosure The first <code>Closure</code> in the chain.
272 * @param secondClosure The second <code>Closure</code> in the chain.
273 * @return A <code>Closure</code> whose {@link Closure#execute execute}
274 * method executes each of the decorated <code>Closure</code>s in
275 * turn.
276 * @throws IllegalArgumentException Thrown if either <code>Closure</code>
277 * argument is <code>null</code>.
278 * @see ChainedClosure
279 */
280 public static <T> Closure<T> chainedClosure(Closure<? super T> firstClosure, Closure<? super T> secondClosure)
281 {
282 if (firstClosure == null) {
283 throw new IllegalArgumentException("null firstClosure not allowed");
284 }
285 if (secondClosure == null) {
286 throw new IllegalArgumentException("null secondClosure not allowed");
287 }
288 Collection<Closure<? super T>> closures = new ArrayList<Closure<? super T>>(2);
289 closures.add(firstClosure);
290 closures.add(secondClosure);
291 return ChainedClosure.getInstance(closures);
292 }
293
294 /***
295 * Decorates an existing <code>Closure</code>, only executing it if a
296 * specified <code>Predicate</code> holds <code>true</code>.
297 *
298 * @param predicate The condition upon which execution of the
299 * <code>Closure</code> is dependent.
300 * @param trueClosure The <code>Closure</code> executed if the
301 * <code>Predicate</code> holds <code>true</code>.
302 * @return A <code>Closure</code> whose {@link Closure#execute execute}
303 * method only executes the decorated <code>Closure</code> if the
304 * <code>Predicate</code> evaluates to <code>true</code>.
305 * @throws IllegalArgumentException Thrown if either argument is
306 * <code>null</code>.
307 * @see IfClosure
308 */
309 public static <T> Closure<T> ifClosure(Predicate<? super T> predicate, Closure<? super T> trueClosure)
310 {
311 return IfClosure.<T>decorate(predicate, trueClosure);
312 }
313
314 /***
315 * Creates a <code>Closure</code> executes one of two existing
316 * <code>Closure</code>s depending upon the outcome of a specified
317 * <code>Predicate</code>.
318 *
319 * @param predicate The condition used to determine which
320 * <code>Closure</code> is executed.
321 * @param trueClosure The <code>Closure</code> executed if the
322 * <code>Predicate</code> holds <code>true</code>.
323 * @param falseClosure The <code>Closure</code> executed if the
324 * <code>Predicate</code> holds <code>false</code>.
325 * @return A <code>Closure</code> whose {@link Closure#execute execute}
326 * method executes the <code>trueClosure</code> if the
327 * <code>Predicate</code> evaluates to <code>true</code>, or the
328 * <code>falseClosure</code> if the <code>Predicate</code> evaluates
329 * to <code>false</code>.
330 * @throws IllegalArgumentException Thrown if any argument is <code>null</code>.
331 * @see IfClosure
332 */
333 public static <T> Closure<T> ifElseClosure(Predicate<? super T> predicate,
334 Closure<? super T> trueClosure, Closure<? super T> falseClosure)
335 {
336 return IfElseClosure.<T>getInstance(predicate, trueClosure, falseClosure);
337 }
338
339 /***
340 * Create a new <code>Closure</code> that calls one of a number of
341 * <code>Closures</code> in a map depending on whether or not the input
342 * object satisfies any of the <code>Predicates</code> in the map.
343 * <p/>
344 * The map consists of <code>Predicate</code> keys and <code>Closure</code>
345 * values. A <code>Closure</code> is executed on the input object if the
346 * input matches its corresponding <code>Predicate</code>. A default
347 * <code>Closure</code> can be specified in the map with a <code>null</code>
348 * key, and is executed on an input object if it doesn't satisfy any of the
349 * other non-null <code>Predicate</code> keys.
350 *
351 * @param predicatesAndClosures A map of <code>Predicate</code>s to
352 * <code>Closure</code>s. An input object is
353 * evaluated against the non-null
354 * <code>Predicate</code> keys, in the order
355 * defined by the entry set's iterator. For the
356 * first <code>Predicate</code> to evaluate to
357 * <code>true</code> against the key, the
358 * corresponding <code>Closure</code> is
359 * executed.
360 * <p/>
361 * If none of the non-null <code>Predicate</code>s
362 * evaluates to <code>true</code>, and a
363 * default <code>Closure</code> is defined, it
364 * is executed against the key, othersize an
365 * <code>IllegalArgumentException</code> will
366 * be thrown.
367 * @return The <code>switch</code> closure based on the contents of the
368 * specified map.
369 * @throws IllegalArgumentException If the map is <code>null</code>.
370 * @throws IllegalArgumentException If any <code>Closure</code> in the map
371 * is <code>null</code>.
372 */
373 public static <T> Closure<T> switchClosure(Map<Predicate<? super T>, Closure<? super T>> predicatesAndClosures)
374 {
375 if (predicatesAndClosures == null) {
376 throw new IllegalArgumentException("null predicatesAndClosures not allowed");
377 }
378 if (predicatesAndClosures.size() == 0) {
379 return NOPClosure.getInstance();
380 }
381 return SwitchClosure.<T>getInstance(predicatesAndClosures);
382 }
383
384 /***
385 * Create a new <code>Closure</code> that calls one of a number of
386 * <code>Closures</code> in a map depending on which key in the map matches
387 * the input object.
388 * <p/>
389 * The map consists of <code>Object</code> keys and <code>Closure</code>
390 * values. If the input object is mapped to a <code>Closure</code>, that
391 * <code>Closure</code> is executed on the input object. If there is no matching
392 * <code>Closure</code> present in the map, a
393 * {@link net.sf.collections15.functors.FunctorException FunctorException} is thrown.
394 *
395 * @param objectsAndClosures A map of <code>Objects</code>s to
396 * <code>Closure</code>s. If an input object is
397 * mapped to a <code>Closure</code> in the map,
398 * that <code>Closure</code> is executed.
399 * @return The <code>switch</code> closure based on the contents of the
400 * specified map.
401 * @throws IllegalArgumentException If the map is <code>null</code>.
402 * @throws IllegalArgumentException If any <code>Closure</code> in the map
403 * is <code>null</code>.
404 */
405 public static <T> Closure<T> switchMapClosure(Map<T, Closure<? super T>> objectsAndClosures)
406 {
407 return SwitchMapClosure.<T>getInstance(objectsAndClosures);
408 }
409
410 /***
411 * Create a new <code>Closure</code> that calls one of a number of
412 * <code>Closures</code> in a map depending on which key in the map matches
413 * the input object.
414 * <p/>
415 * The map consists of <code>Object</code> keys and <code>Closure</code>
416 * values. If the input object is mapped to a <code>Closure</code>, that
417 * <code>Closure</code> is executed on the input object. If the input object
418 * doesn't map to any <code>Closure</code>, the specified <code>defaultClosure</code>
419 * is executed.
420 *
421 * @param objectsAndClosures A map of <code>Objects</code>s to
422 * <code>Closure</code>s. If an input object is
423 * mapped to a <code>Closure</code> in the map,
424 * that <code>Closure</code> is executed.
425 * @param defaultClosure If an input object doesn't map to any of the <code>Closure</code>s
426 * defined in <code>objectsAndClosures</code>, then this <code>Closure</code> is executed.
427 * @return The <code>switch</code> closure based on the contents of the
428 * specified map.
429 * @throws IllegalArgumentException If either argument is <code>null</code>.
430 * @throws IllegalArgumentException If any <code>Closure</code> in the map
431 * is <code>null</code>.
432 */
433 public static <T> Closure<T> switchMapClosure(Map<T, Closure<? super T>> objectsAndClosures, Closure<? super T> defaultClosure)
434 {
435 return SwitchMapClosure.<T>getInstance(objectsAndClosures, defaultClosure);
436 }
437 }