    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}