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 *      https://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.LinkedHashMap;
021import java.util.Map;
022import java.util.Objects;
023
024import org.apache.commons.collections4.functors.ChainedClosure;
025import org.apache.commons.collections4.functors.EqualPredicate;
026import org.apache.commons.collections4.functors.ExceptionClosure;
027import org.apache.commons.collections4.functors.ForClosure;
028import org.apache.commons.collections4.functors.IfClosure;
029import org.apache.commons.collections4.functors.InvokerTransformer;
030import org.apache.commons.collections4.functors.NOPClosure;
031import org.apache.commons.collections4.functors.SwitchClosure;
032import org.apache.commons.collections4.functors.TransformerClosure;
033import org.apache.commons.collections4.functors.WhileClosure;
034
035/**
036 * {@code ClosureUtils} provides reference implementations and utilities
037 * for the Closure functor interface. The supplied closures are:
038 * <ul>
039 * <li>Invoker - invokes a method on the input object</li>
040 * <li>For - repeatedly calls a closure for a fixed number of times</li>
041 * <li>While - repeatedly calls a closure while a predicate is true</li>
042 * <li>Chained - chains two or more closures together</li>
043 * <li>If - calls one closure or another based on a predicate</li>
044 * <li>Switch - calls one closure based on one or more predicates</li>
045 * <li>SwitchMap - calls one closure looked up from a Map</li>
046 * <li>Transformer - wraps a Transformer as a Closure</li>
047 * <li>NOP - does nothing</li>
048 * <li>Exception - always throws an exception</li>
049 * </ul>
050 * <p>
051 * Since v4.1 only closures which are considered to be safe are
052 * Serializable. Closures considered to be unsafe for serialization are:
053 * </p>
054 * <ul>
055 * <li>Invoker</li>
056 * <li>For</li>
057 * <li>While</li>
058 * </ul>
059 *
060 * @since 3.0
061 */
062public class ClosureUtils {
063
064    /**
065     * Creates a Closure that calls a Transformer each time it is called.
066     * The transformer will be called using the closure's input object.
067     * The transformer's result will be ignored.
068     *
069     * @see org.apache.commons.collections4.functors.TransformerClosure
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     * @param <E>  the type that the closure acts on
084     * @param closures  An array of closures to chain
085     * @return The {@code chained} closure
086     * @throws NullPointerException if the closures array is null
087     * @throws NullPointerException if any closure in the array is null
088     */
089    public static <E> Closure<E> chainedClosure(final Closure<? super E>... closures) {
090        return ChainedClosure.chainedClosure(closures);
091    }
092
093    /**
094     * Create a new Closure that calls each closure in turn, passing the
095     * result into the next closure. The ordering is that of the iterator()
096     * method on the collection.
097     *
098     * @see org.apache.commons.collections4.functors.ChainedClosure
099     * @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}