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