View Javadoc

1   /*
2    *  Copyright 2002-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
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> &lt; 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         // reuse transformer as it has caching - this is lazy really, should have inner class here
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         // reuse transformer as it has caching - this is lazy really, should have inner class here
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 }