001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4;
018
019import java.util.Collection;
020import java.util.Map;
021import java.util.Objects;
022
023import org.apache.commons.collections4.functors.ChainedClosure;
024import org.apache.commons.collections4.functors.EqualPredicate;
025import org.apache.commons.collections4.functors.ExceptionClosure;
026import org.apache.commons.collections4.functors.ForClosure;
027import org.apache.commons.collections4.functors.IfClosure;
028import org.apache.commons.collections4.functors.InvokerTransformer;
029import org.apache.commons.collections4.functors.NOPClosure;
030import org.apache.commons.collections4.functors.SwitchClosure;
031import org.apache.commons.collections4.functors.TransformerClosure;
032import org.apache.commons.collections4.functors.WhileClosure;
033
034/**
035 * {@code ClosureUtils} provides reference implementations and utilities
036 * for the Closure functor interface. The supplied closures are:
037 * <ul>
038 * <li>Invoker - invokes a method on the input object
039 * <li>For - repeatedly calls a closure for a fixed number of times
040 * <li>While - repeatedly calls a closure while a predicate is true
041 * <li>Chained - chains two or more closures together
042 * <li>If - calls one closure or another based on a predicate
043 * <li>Switch - calls one closure based on one or more predicates
044 * <li>SwitchMap - calls one closure looked up from a Map
045 * <li>Transformer - wraps a Transformer as a Closure
046 * <li>NOP - does nothing
047 * <li>Exception - always throws an exception
048 * </ul>
049 * <p>
050 * Since v4.1 only closures which are considered to be safe are
051 * Serializable. Closures considered to be unsafe for serialization are:
052 * </p>
053 * <ul>
054 * <li>Invoker
055 * <li>For
056 * <li>While
057 * </ul>
058 *
059 * @since 3.0
060 */
061public class ClosureUtils {
062
063    /**
064     * Creates a Closure that calls a Transformer each time it is called.
065     * The transformer will be called using the closure's input object.
066     * The transformer's result will be ignored.
067     *
068     * @see org.apache.commons.collections4.functors.TransformerClosure
069     *
070     * @param <E>  the type that the closure acts on
071     * @param transformer  the transformer to run each time in the closure, null means nop
072     * @return the closure
073     */
074    public static <E> Closure<E> asClosure(final Transformer<? super E, ?> transformer) {
075        return TransformerClosure.transformerClosure(transformer);
076    }
077
078    /**
079     * Create a new Closure that calls each closure in turn, passing the
080     * result into the next closure.
081     *
082     * @see org.apache.commons.collections4.functors.ChainedClosure
083     *
084     * @param <E>  the type that the closure acts on
085     * @param closures  an array of closures to chain
086     * @return the {@code chained} closure
087     * @throws NullPointerException if the closures array is null
088     * @throws NullPointerException if any closure in the array is null
089     */
090    public static <E> Closure<E> chainedClosure(final Closure<? super E>... closures) {
091        return ChainedClosure.chainedClosure(closures);
092    }
093
094    /**
095     * Create a new Closure that calls each closure in turn, passing the
096     * result into the next closure. The ordering is that of the iterator()
097     * method on the collection.
098     *
099     * @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        // empty
375    }
376
377}