View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.commons.collections;
18  
19  import java.util.Collection;
20  import java.util.Iterator;
21  import java.util.Map;
22  
23  import org.apache.commons.collections.functors.ChainedClosure;
24  import org.apache.commons.collections.functors.EqualPredicate;
25  import org.apache.commons.collections.functors.ExceptionClosure;
26  import org.apache.commons.collections.functors.ForClosure;
27  import org.apache.commons.collections.functors.IfClosure;
28  import org.apache.commons.collections.functors.InvokerTransformer;
29  import org.apache.commons.collections.functors.NOPClosure;
30  import org.apache.commons.collections.functors.SwitchClosure;
31  import org.apache.commons.collections.functors.TransformerClosure;
32  import org.apache.commons.collections.functors.WhileClosure;
33  
34  /**
35   * <code>ClosureUtils</code> provides reference implementations and utilities
36   * for the Closure functor interface. The supplied closures are:
37   * <ul>
38   * <li>Invoker - invokes a method on the input object
39   * <li>For - repeatedly calls a closure for a fixed number of times
40   * <li>While - repeatedly calls a closure while a predicate is true
41   * <li>DoWhile - repeatedly calls a closure while a predicate is true
42   * <li>Chained - chains two or more closures together
43   * <li>Switch - calls one closure based on one or more predicates
44   * <li>SwitchMap - calls one closure looked up from a Map
45   * <li>Transformer - wraps a Transformer as a Closure
46   * <li>NOP - does nothing
47   * <li>Exception - always throws an exception
48   * </ul>
49   * All the supplied closures are Serializable.
50   * 
51   * @since Commons Collections 3.0
52   * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
53   *
54   * @author Stephen Colebourne
55   * @author Matt Benson
56   */
57  public class ClosureUtils {
58  
59      /**
60       * This class is not normally instantiated.
61       */
62      public ClosureUtils() {
63          super();
64      }
65  
66      /**
67       * Gets a Closure that always throws an exception.
68       * This could be useful during testing as a placeholder.
69       *
70       * @see org.apache.commons.collections.functors.ExceptionClosure
71       * 
72       * @return the closure
73       */
74      public static Closure exceptionClosure() {
75          return ExceptionClosure.INSTANCE;
76      }
77  
78      /**
79       * Gets a Closure that will do nothing.
80       * This could be useful during testing as a placeholder.
81       *
82       * @see org.apache.commons.collections.functors.NOPClosure
83       * 
84       * @return the closure
85       */
86      public static Closure nopClosure() {
87          return NOPClosure.INSTANCE;
88      }
89  
90      /**
91       * Creates a Closure that calls a Transformer each time it is called.
92       * The transformer will be called using the closure's input object.
93       * The transformer's result will be ignored.
94       *
95       * @see org.apache.commons.collections.functors.TransformerClosure
96       * 
97       * @param transformer  the transformer to run each time in the closure, null means nop
98       * @return the closure
99       */
100     public static Closure asClosure(Transformer transformer) {
101         return TransformerClosure.getInstance(transformer);
102     }
103 
104     /**
105      * Creates a Closure that will call the closure <code>count</code> times.
106      * <p>
107      * A null closure or zero count returns the <code>NOPClosure</code>.
108      *
109      * @see org.apache.commons.collections.functors.ForClosure
110      * 
111      * @param count  the number of times to loop
112      * @param closure  the closure to call repeatedly
113      * @return the <code>for</code> closure
114      */
115     public static Closure forClosure(int count, Closure closure) {
116         return ForClosure.getInstance(count, closure);
117     }
118 
119     /**
120      * Creates a Closure that will call the closure repeatedly until the 
121      * predicate returns false.
122      *
123      * @see org.apache.commons.collections.functors.WhileClosure
124      * 
125      * @param predicate  the predicate to use as an end of loop test, not null
126      * @param closure  the closure to call repeatedly, not null
127      * @return the <code>while</code> closure
128      * @throws IllegalArgumentException if either argument is null
129      */
130     public static Closure whileClosure(Predicate predicate, Closure closure) {
131         return WhileClosure.getInstance(predicate, closure, false);
132     }
133 
134     /**
135      * Creates a Closure that will call the closure once and then repeatedly
136      * until the predicate returns false.
137      *
138      * @see org.apache.commons.collections.functors.WhileClosure
139      * 
140      * @param closure  the closure to call repeatedly, not null
141      * @param predicate  the predicate to use as an end of loop test, not null
142      * @return the <code>do-while</code> closure
143      * @throws IllegalArgumentException if either argument is null
144      */
145     public static Closure doWhileClosure(Closure closure, Predicate predicate) {
146         return WhileClosure.getInstance(predicate, closure, true);
147     }
148 
149     /**
150      * Creates a Closure that will invoke a specific method on the closure's
151      * input object by reflection.
152      *
153      * @see org.apache.commons.collections.functors.InvokerTransformer
154      * @see org.apache.commons.collections.functors.TransformerClosure
155      * 
156      * @param methodName  the name of the method
157      * @return the <code>invoker</code> closure
158      * @throws IllegalArgumentException if the method name is null
159      */
160     public static Closure invokerClosure(String methodName) {
161         // reuse transformer as it has caching - this is lazy really, should have inner class here
162         return asClosure(InvokerTransformer.getInstance(methodName));
163     }
164 
165     /**
166      * Creates a Closure that will invoke a specific method on the closure's
167      * input object by reflection.
168      *
169      * @see org.apache.commons.collections.functors.InvokerTransformer
170      * @see org.apache.commons.collections.functors.TransformerClosure
171      * 
172      * @param methodName  the name of the method
173      * @param paramTypes  the parameter types
174      * @param args  the arguments
175      * @return the <code>invoker</code> closure
176      * @throws IllegalArgumentException if the method name is null
177      * @throws IllegalArgumentException if the paramTypes and args don't match
178      */
179     public static Closure invokerClosure(String methodName, Class[] paramTypes, Object[] args) {
180         // reuse transformer as it has caching - this is lazy really, should have inner class here
181         return asClosure(InvokerTransformer.getInstance(methodName, paramTypes, args));
182     }
183 
184     /**
185      * Create a new Closure that calls two Closures, passing the result of
186      * the first into the second.
187      * 
188      * @see org.apache.commons.collections.functors.ChainedClosure
189      * 
190      * @param closure1  the first closure
191      * @param closure2  the second closure
192      * @return the <code>chained</code> closure
193      * @throws IllegalArgumentException if either closure is null
194      */
195     public static Closure chainedClosure(Closure closure1, Closure closure2) {
196         return ChainedClosure.getInstance(closure1, closure2);
197     }
198 
199     /**
200      * Create a new Closure that calls each closure in turn, passing the 
201      * result into the next closure.
202      * 
203      * @see org.apache.commons.collections.functors.ChainedClosure
204      * 
205      * @param closures  an array of closures to chain
206      * @return the <code>chained</code> closure
207      * @throws IllegalArgumentException if the closures array is null
208      * @throws IllegalArgumentException if any closure in the array is null
209      */
210     public static Closure chainedClosure(Closure[] closures) {
211         return ChainedClosure.getInstance(closures);
212     }
213 
214     /**
215      * Create a new Closure that calls each closure in turn, passing the 
216      * result into the next closure. The ordering is that of the iterator()
217      * method on the collection.
218      * 
219      * @see org.apache.commons.collections.functors.ChainedClosure
220      * 
221      * @param closures  a collection of closures to chain
222      * @return the <code>chained</code> closure
223      * @throws IllegalArgumentException if the closures collection is null
224      * @throws IllegalArgumentException if the closures collection is empty
225      * @throws IllegalArgumentException if any closure in the collection is null
226      */
227     public static Closure chainedClosure(Collection closures) {
228         return ChainedClosure.getInstance(closures);
229     }
230 
231     /**
232      * Create a new Closure that calls another closure based on the
233      * result of the specified predicate.
234      * 
235      * @see org.apache.commons.collections.functors.IfClosure
236      * 
237      * @param predicate  the validating predicate
238      * @param trueClosure  the closure called if the predicate is true
239      * @return the <code>if</code> closure
240      * @throws IllegalArgumentException if the predicate is null
241      * @throws IllegalArgumentException if the closure is null
242      * @since Commons Collections 3.2
243      */
244     public static Closure ifClosure(Predicate predicate, Closure trueClosure) {
245         return IfClosure.getInstance(predicate, trueClosure);
246     }
247 
248     /**
249      * Create a new Closure that calls one of two closures depending 
250      * on the specified predicate.
251      * 
252      * @see org.apache.commons.collections.functors.IfClosure
253      * 
254      * @param predicate  the predicate to switch on
255      * @param trueClosure  the closure called if the predicate is true
256      * @param falseClosure  the closure called if the predicate is false
257      * @return the <code>switch</code> closure
258      * @throws IllegalArgumentException if the predicate is null
259      * @throws IllegalArgumentException if either closure is null
260      */
261     public static Closure ifClosure(Predicate predicate, Closure trueClosure, Closure falseClosure) {
262         return IfClosure.getInstance(predicate, trueClosure, falseClosure);
263     }
264 
265     /**
266      * Create a new Closure that calls one of the closures depending 
267      * on the predicates.
268      * <p>
269      * The closure at array location 0 is called if the predicate at array 
270      * location 0 returned true. Each predicate is evaluated
271      * until one returns true.
272      * 
273      * @see org.apache.commons.collections.functors.SwitchClosure
274      * 
275      * @param predicates  an array of predicates to check, not null
276      * @param closures  an array of closures to call, not null
277      * @return the <code>switch</code> closure
278      * @throws IllegalArgumentException if the either array is null
279      * @throws IllegalArgumentException if any element in the arrays is null
280      * @throws IllegalArgumentException if the arrays are different sizes
281      */
282     public static Closure switchClosure(Predicate[] predicates, Closure[] closures) {
283         return SwitchClosure.getInstance(predicates, closures, null);
284     }
285 
286     /**
287      * Create a new Closure that calls one of the closures depending 
288      * on the predicates.
289      * <p>
290      * The closure at array location 0 is called if the predicate at array
291      * location 0 returned true. Each predicate is evaluated
292      * until one returns true. If no predicates evaluate to true, the default
293      * closure is called.
294      * 
295      * @see org.apache.commons.collections.functors.SwitchClosure
296      * 
297      * @param predicates  an array of predicates to check, not null
298      * @param closures  an array of closures to call, not null
299      * @param defaultClosure  the default to call if no predicate matches
300      * @return the <code>switch</code> closure
301      * @throws IllegalArgumentException if the either array is null
302      * @throws IllegalArgumentException if any element in the arrays is null
303      * @throws IllegalArgumentException if the arrays are different sizes
304      */
305     public static Closure switchClosure(Predicate[] predicates, Closure[] closures, Closure defaultClosure) {
306         return SwitchClosure.getInstance(predicates, closures, defaultClosure);
307     }
308     
309     /**
310      * Create a new Closure that calls one of the closures depending 
311      * on the predicates. 
312      * <p>
313      * The Map consists of Predicate keys and Closure values. A closure 
314      * is called if its matching predicate returns true. Each predicate is evaluated
315      * until one returns true. If no predicates evaluate to true, the default
316      * closure is called. The default closure is set in the map with a 
317      * null key. The ordering is that of the iterator() method on the entryset 
318      * collection of the map.
319      * 
320      * @see org.apache.commons.collections.functors.SwitchClosure
321      * 
322      * @param predicatesAndClosures  a map of predicates to closures
323      * @return the <code>switch</code> closure
324      * @throws IllegalArgumentException if the map is null
325      * @throws IllegalArgumentException if the map is empty
326      * @throws IllegalArgumentException if any closure in the map is null
327      * @throws ClassCastException  if the map elements are of the wrong type
328      */
329     public static Closure switchClosure(Map predicatesAndClosures) {
330         return SwitchClosure.getInstance(predicatesAndClosures);
331     }
332 
333     /**
334      * Create a new Closure that uses the input object as a key to find the
335      * closure to call. 
336      * <p>
337      * The Map consists of object keys and Closure values. A closure 
338      * is called if the input object equals the key. If there is no match, the
339      * default closure is called. The default closure is set in the map
340      * using a null key.
341      * 
342      * @see org.apache.commons.collections.functors.SwitchClosure
343      * 
344      * @param objectsAndClosures  a map of objects to closures
345      * @return the closure
346      * @throws IllegalArgumentException if the map is null
347      * @throws IllegalArgumentException if the map is empty
348      * @throws IllegalArgumentException if any closure in the map is null
349      */
350     public static Closure switchMapClosure(Map objectsAndClosures) {
351         Closure[] trs = null;
352         Predicate[] preds = null;
353         if (objectsAndClosures == null) {
354             throw new IllegalArgumentException("The object and closure map must not be null");
355         }
356         Closure def = (Closure) objectsAndClosures.remove(null);
357         int size = objectsAndClosures.size();
358         trs = new Closure[size];
359         preds = new Predicate[size];
360         int i = 0;
361         for (Iterator it = objectsAndClosures.entrySet().iterator(); it.hasNext();) {
362             Map.Entry entry = (Map.Entry) it.next();
363             preds[i] = EqualPredicate.getInstance(entry.getKey());
364             trs[i] = (Closure) entry.getValue();
365             i++;
366         }
367         return switchClosure(preds, trs, def);
368     }
369 
370 }