ClosureUtils.java

  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. import java.util.Collection;
  19. import java.util.Map;
  20. import java.util.Objects;

  21. import org.apache.commons.collections4.functors.ChainedClosure;
  22. import org.apache.commons.collections4.functors.EqualPredicate;
  23. import org.apache.commons.collections4.functors.ExceptionClosure;
  24. import org.apache.commons.collections4.functors.ForClosure;
  25. import org.apache.commons.collections4.functors.IfClosure;
  26. import org.apache.commons.collections4.functors.InvokerTransformer;
  27. import org.apache.commons.collections4.functors.NOPClosure;
  28. import org.apache.commons.collections4.functors.SwitchClosure;
  29. import org.apache.commons.collections4.functors.TransformerClosure;
  30. import org.apache.commons.collections4.functors.WhileClosure;

  31. /**
  32.  * {@code ClosureUtils} provides reference implementations and utilities
  33.  * for the Closure functor interface. The supplied closures are:
  34.  * <ul>
  35.  * <li>Invoker - invokes a method on the input object
  36.  * <li>For - repeatedly calls a closure for a fixed number of times
  37.  * <li>While - repeatedly calls a closure while a predicate is true
  38.  * <li>Chained - chains two or more closures together
  39.  * <li>If - calls one closure or another based on a predicate
  40.  * <li>Switch - calls one closure based on one or more predicates
  41.  * <li>SwitchMap - calls one closure looked up from a Map
  42.  * <li>Transformer - wraps a Transformer as a Closure
  43.  * <li>NOP - does nothing
  44.  * <li>Exception - always throws an exception
  45.  * </ul>
  46.  * <p>
  47.  * Since v4.1 only closures which are considered to be safe are
  48.  * Serializable. Closures considered to be unsafe for serialization are:
  49.  * </p>
  50.  * <ul>
  51.  * <li>Invoker
  52.  * <li>For
  53.  * <li>While
  54.  * </ul>
  55.  *
  56.  * @since 3.0
  57.  */
  58. public class ClosureUtils {

  59.     /**
  60.      * Creates a Closure that calls a Transformer each time it is called.
  61.      * The transformer will be called using the closure's input object.
  62.      * The transformer's result will be ignored.
  63.      *
  64.      * @see org.apache.commons.collections4.functors.TransformerClosure
  65.      * @param <E>  the type that the closure acts on
  66.      * @param transformer  the transformer to run each time in the closure, null means nop
  67.      * @return the closure
  68.      */
  69.     public static <E> Closure<E> asClosure(final Transformer<? super E, ?> transformer) {
  70.         return TransformerClosure.transformerClosure(transformer);
  71.     }

  72.     /**
  73.      * Create a new Closure that calls each closure in turn, passing the
  74.      * result into the next closure.
  75.      *
  76.      * @see org.apache.commons.collections4.functors.ChainedClosure
  77.      * @param <E>  the type that the closure acts on
  78.      * @param closures  an array of closures to chain
  79.      * @return the {@code chained} closure
  80.      * @throws NullPointerException if the closures array is null
  81.      * @throws NullPointerException if any closure in the array is null
  82.      */
  83.     public static <E> Closure<E> chainedClosure(final Closure<? super E>... closures) {
  84.         return ChainedClosure.chainedClosure(closures);
  85.     }

  86.     /**
  87.      * Create a new Closure that calls each closure in turn, passing the
  88.      * result into the next closure. The ordering is that of the iterator()
  89.      * method on the collection.
  90.      *
  91.      * @see org.apache.commons.collections4.functors.ChainedClosure
  92.      * @param <E>  the type that the closure acts on
  93.      * @param closures  a collection of closures to chain
  94.      * @return the {@code chained} closure
  95.      * @throws NullPointerException if the closures collection is null
  96.      * @throws NullPointerException if any closure in the collection is null
  97.      */
  98.     public static <E> Closure<E> chainedClosure(final Collection<? extends Closure<? super E>> closures) {
  99.         return ChainedClosure.chainedClosure(closures);
  100.     }

  101.     /**
  102.      * Creates a Closure that will call the closure once and then repeatedly
  103.      * until the predicate returns false.
  104.      *
  105.      * @see org.apache.commons.collections4.functors.WhileClosure
  106.      * @param <E>  the type that the closure acts on
  107.      * @param closure  the closure to call repeatedly, not null
  108.      * @param predicate  the predicate to use as an end of loop test, not null
  109.      * @return the {@code do-while} closure
  110.      * @throws NullPointerException if either argument is null
  111.      */
  112.     public static <E> Closure<E> doWhileClosure(final Closure<? super E> closure,
  113.                                                 final Predicate<? super E> predicate) {
  114.         return WhileClosure.<E>whileClosure(predicate, closure, true);
  115.     }

  116.     /**
  117.      * Gets a Closure that always throws an exception.
  118.      * This could be useful during testing as a placeholder.
  119.      *
  120.      * @param <E>  the type that the closure acts on
  121.      * @return the closure
  122.      * @see ExceptionClosure
  123.      */
  124.     public static <E> Closure<E> exceptionClosure() {
  125.         return ExceptionClosure.<E>exceptionClosure();
  126.     }

  127.     /**
  128.      * Creates a Closure that will call the closure {@code count} times.
  129.      * <p>
  130.      * A null closure or zero count returns the {@code NOPClosure}.
  131.      *
  132.      * @see org.apache.commons.collections4.functors.ForClosure
  133.      * @param <E>  the type that the closure acts on
  134.      * @param count  the number of times to loop
  135.      * @param closure  the closure to call repeatedly
  136.      * @return the {@code for} closure
  137.      */
  138.     public static <E> Closure<E> forClosure(final int count, final Closure<? super E> closure) {
  139.         return ForClosure.forClosure(count, closure);
  140.     }

  141.     /**
  142.      * Create a new Closure that calls another closure based on the
  143.      * result of the specified predicate.
  144.      *
  145.      * @see org.apache.commons.collections4.functors.IfClosure
  146.      * @param <E>  the type that the closure acts on
  147.      * @param predicate  the validating predicate
  148.      * @param trueClosure  the closure called if the predicate is true
  149.      * @return the {@code if} closure
  150.      * @throws NullPointerException if the predicate or closure is null
  151.      * @since 3.2
  152.      */
  153.     public static <E> Closure<E> ifClosure(final Predicate<? super E> predicate,
  154.                                            final Closure<? super E> trueClosure) {
  155.         return IfClosure.<E>ifClosure(predicate, trueClosure);
  156.     }

  157.     /**
  158.      * Create a new Closure that calls one of two closures depending
  159.      * on the specified predicate.
  160.      *
  161.      * @see org.apache.commons.collections4.functors.IfClosure
  162.      * @param <E>  the type that the closure acts on
  163.      * @param predicate  the predicate to switch on
  164.      * @param trueClosure  the closure called if the predicate is true
  165.      * @param falseClosure  the closure called if the predicate is false
  166.      * @return the {@code switch} closure
  167.      * @throws NullPointerException if the predicate or either closure is null
  168.      */
  169.     public static <E> Closure<E> ifClosure(final Predicate<? super E> predicate,
  170.                                            final Closure<? super E> trueClosure,
  171.                                            final Closure<? super E> falseClosure) {
  172.         return IfClosure.<E>ifClosure(predicate, trueClosure, falseClosure);
  173.     }

  174.     /**
  175.      * Creates a Closure that will invoke a specific method on the closure's
  176.      * input object by reflection.
  177.      *
  178.      * @see org.apache.commons.collections4.functors.InvokerTransformer
  179.      * @see org.apache.commons.collections4.functors.TransformerClosure
  180.      * @param <E>  the type that the closure acts on
  181.      * @param methodName  the name of the method
  182.      * @return the {@code invoker} closure
  183.      * @throws NullPointerException if the method name is null
  184.      */
  185.     public static <E> Closure<E> invokerClosure(final String methodName) {
  186.         // reuse transformer as it has caching - this is lazy really, should have inner class here
  187.         return asClosure(InvokerTransformer.<E, Object>invokerTransformer(methodName));
  188.     }

  189.     /**
  190.      * Creates a Closure that will invoke a specific method on the closure's
  191.      * input object by reflection.
  192.      *
  193.      * @see org.apache.commons.collections4.functors.InvokerTransformer
  194.      * @see org.apache.commons.collections4.functors.TransformerClosure
  195.      * @param <E>  the type that the closure acts on
  196.      * @param methodName  the name of the method
  197.      * @param paramTypes  the parameter types
  198.      * @param args  the arguments
  199.      * @return the {@code invoker} closure
  200.      * @throws NullPointerException if the method name is null
  201.      * @throws IllegalArgumentException if the paramTypes and args don't match
  202.      */
  203.     public static <E> Closure<E> invokerClosure(final String methodName, final Class<?>[] paramTypes,
  204.                                                 final Object[] args) {
  205.         // reuse transformer as it has caching - this is lazy really, should have inner class here
  206.         return asClosure(InvokerTransformer.<E, Object>invokerTransformer(methodName, paramTypes, args));
  207.     }

  208.     /**
  209.      * Gets a Closure that will do nothing.
  210.      * This could be useful during testing as a placeholder.
  211.      *
  212.      * @see org.apache.commons.collections4.functors.NOPClosure
  213.      * @param <E>  the type that the closure acts on
  214.      * @return the closure
  215.      */
  216.     public static <E> Closure<E> nopClosure() {
  217.         return NOPClosure.<E>nopClosure();
  218.     }

  219.     /**
  220.      * Create a new Closure that calls one of the closures depending
  221.      * on the predicates.
  222.      * <p>
  223.      * The Map consists of Predicate keys and Closure values. A closure
  224.      * is called if its matching predicate returns true. Each predicate is evaluated
  225.      * until one returns true. If no predicates evaluate to true, the default
  226.      * closure is called. The default closure is set in the map with a
  227.      * null key. The ordering is that of the iterator() method on the entryset
  228.      * collection of the map.
  229.      * </p>
  230.      *
  231.      * @see org.apache.commons.collections4.functors.SwitchClosure
  232.      * @param <E>  the type that the closure acts on
  233.      * @param predicatesAndClosures  a map of predicates to closures
  234.      * @return the {@code switch} closure
  235.      * @throws NullPointerException if the map is null
  236.      * @throws NullPointerException if any closure in the map is null
  237.      * @throws ClassCastException  if the map elements are of the wrong type
  238.      */
  239.     public static <E> Closure<E> switchClosure(final Map<Predicate<E>, Closure<E>> predicatesAndClosures) {
  240.         return SwitchClosure.switchClosure(predicatesAndClosures);
  241.     }

  242.     /**
  243.      * Create a new Closure that calls one of the closures depending
  244.      * on the predicates.
  245.      * <p>
  246.      * The closure at array location 0 is called if the predicate at array
  247.      * location 0 returned true. Each predicate is evaluated
  248.      * until one returns true.
  249.      * </p>
  250.      *
  251.      * @see org.apache.commons.collections4.functors.SwitchClosure
  252.      * @param <E>  the type that the closure acts on
  253.      * @param predicates  an array of predicates to check, not null
  254.      * @param closures  an array of closures to call, not null
  255.      * @return the {@code switch} closure
  256.      * @throws NullPointerException if either array is null
  257.      * @throws NullPointerException if any element in the arrays is null
  258.      * @throws IllegalArgumentException if the arrays have different sizes
  259.      */
  260.     public static <E> Closure<E> switchClosure(final Predicate<? super E>[] predicates,
  261.                                                final Closure<? super E>[] closures) {
  262.         return SwitchClosure.<E>switchClosure(predicates, closures, null);
  263.     }

  264.     /**
  265.      * Create a new Closure that calls one of the closures depending
  266.      * on the predicates.
  267.      * <p>
  268.      * The closure at array location 0 is called if the predicate at array
  269.      * location 0 returned true. Each predicate is evaluated
  270.      * until one returns true. If no predicates evaluate to true, the default
  271.      * closure is called.
  272.      * </p>
  273.      *
  274.      * @see org.apache.commons.collections4.functors.SwitchClosure
  275.      * @param <E>  the type that the closure acts on
  276.      * @param predicates  an array of predicates to check, not null
  277.      * @param closures  an array of closures to call, not null
  278.      * @param defaultClosure  the default to call if no predicate matches
  279.      * @return the {@code switch} closure
  280.      * @throws NullPointerException if either array is null
  281.      * @throws NullPointerException if any element in the arrays is null
  282.      * @throws IllegalArgumentException if the arrays are different sizes
  283.      */
  284.     public static <E> Closure<E> switchClosure(final Predicate<? super E>[] predicates,
  285.                                                final Closure<? super E>[] closures,
  286.                                                final Closure<? super E> defaultClosure) {
  287.         return SwitchClosure.<E>switchClosure(predicates, closures, defaultClosure);
  288.     }

  289.     /**
  290.      * Create a new Closure that uses the input object as a key to find the
  291.      * closure to call.
  292.      * <p>
  293.      * The Map consists of object keys and Closure values. A closure
  294.      * is called if the input object equals the key. If there is no match, the
  295.      * default closure is called. The default closure is set in the map
  296.      * using a null key.
  297.      * </p>
  298.      *
  299.      * @see org.apache.commons.collections4.functors.SwitchClosure
  300.      * @param <E>  the type that the closure acts on
  301.      * @param objectsAndClosures  a map of objects to closures
  302.      * @return the closure
  303.      * @throws NullPointerException if the map is null
  304.      * @throws NullPointerException if any closure in the map is null
  305.      */
  306.     @SuppressWarnings("unchecked")
  307.     public static <E> Closure<E> switchMapClosure(final Map<? extends E, Closure<E>> objectsAndClosures) {
  308.         Objects.requireNonNull(objectsAndClosures, "objectsAndClosures");
  309.         final Closure<? super E> def = objectsAndClosures.remove(null);
  310.         final int size = objectsAndClosures.size();
  311.         final Closure<? super E>[] trs = new Closure[size];
  312.         final Predicate<E>[] preds = new Predicate[size];
  313.         int i = 0;
  314.         for (final Map.Entry<? extends E, Closure<E>> entry : objectsAndClosures.entrySet()) {
  315.             preds[i] = EqualPredicate.<E>equalPredicate(entry.getKey());
  316.             trs[i] = entry.getValue();
  317.             i++;
  318.         }
  319.         return ClosureUtils.<E>switchClosure(preds, trs, def);
  320.     }

  321.     /**
  322.      * Creates a Closure that will call the closure repeatedly until the
  323.      * predicate returns false.
  324.      *
  325.      * @see org.apache.commons.collections4.functors.WhileClosure
  326.      * @param <E>  the type that the closure acts on
  327.      * @param predicate  the predicate to use as an end of loop test, not null
  328.      * @param closure  the closure to call repeatedly, not null
  329.      * @return the {@code while} closure
  330.      * @throws NullPointerException if either argument is null
  331.      */
  332.     public static <E> Closure<E> whileClosure(final Predicate<? super E> predicate, final Closure<? super E> closure) {
  333.         return WhileClosure.<E>whileClosure(predicate, closure, false);
  334.     }

  335.     /**
  336.      * Don't allow instances.
  337.      */
  338.     private ClosureUtils() {
  339.         // empty
  340.     }

  341. }