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