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