SwitchClosure.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.functors;

  18. import java.io.Serializable;
  19. import java.util.Map;
  20. import java.util.Objects;

  21. import org.apache.commons.collections4.Closure;
  22. import org.apache.commons.collections4.Predicate;

  23. /**
  24.  * Closure implementation calls the closure whose predicate returns true,
  25.  * like a switch statement.
  26.  *
  27.  * @param <T> the type of the input to the operation.
  28.  * @since 3.0
  29.  */
  30. public class SwitchClosure<T> implements Closure<T>, Serializable {

  31.     /** Serial version UID */
  32.     private static final long serialVersionUID = 3518477308466486130L;

  33.     /**
  34.      * Create a new Closure that calls one of the closures depending
  35.      * on the predicates.
  36.      * <p>
  37.      * The Map consists of Predicate keys and Closure values. A closure
  38.      * is called if its matching predicate returns true. Each predicate is evaluated
  39.      * until one returns true. If no predicates evaluate to true, the default
  40.      * closure is called. The default closure is set in the map with a
  41.      * null key. The ordering is that of the iterator() method on the entryset
  42.      * collection of the map.
  43.      *
  44.      * @param <E> the type that the closure acts on
  45.      * @param predicatesAndClosures  a map of predicates to closures
  46.      * @return the {@code switch} closure
  47.      * @throws NullPointerException if the map is null
  48.      * @throws NullPointerException if any closure in the map is null
  49.      * @throws ClassCastException  if the map elements are of the wrong type
  50.      */
  51.     @SuppressWarnings("unchecked")
  52.     public static <E> Closure<E> switchClosure(final Map<Predicate<E>, Closure<E>> predicatesAndClosures) {
  53.         Objects.requireNonNull(predicatesAndClosures, "predicatesAndClosures");
  54.         // convert to array like this to guarantee iterator() ordering
  55.         final Closure<? super E> defaultClosure = predicatesAndClosures.remove(null);
  56.         final int size = predicatesAndClosures.size();
  57.         if (size == 0) {
  58.             return (Closure<E>) (defaultClosure == null ? NOPClosure.<E>nopClosure() : defaultClosure);
  59.         }
  60.         final Closure<E>[] closures = new Closure[size];
  61.         final Predicate<E>[] preds = new Predicate[size];
  62.         int i = 0;
  63.         for (final Map.Entry<Predicate<E>, Closure<E>> entry : predicatesAndClosures.entrySet()) {
  64.             preds[i] = entry.getKey();
  65.             closures[i] = entry.getValue();
  66.             i++;
  67.         }
  68.         return new SwitchClosure<>(false, preds, closures, defaultClosure);
  69.     }
  70.     /**
  71.      * Factory method that performs validation and copies the parameter arrays.
  72.      *
  73.      * @param <E> the type that the closure acts on
  74.      * @param predicates  array of predicates, cloned, no nulls
  75.      * @param closures  matching array of closures, cloned, no nulls
  76.      * @param defaultClosure  the closure to use if no match, null means nop
  77.      * @return the {@code chained} closure
  78.      * @throws NullPointerException if array is null
  79.      * @throws NullPointerException if any element in the array is null
  80.      * @throws IllegalArgumentException if the array lengths of predicates and closures do not match
  81.      */
  82.     @SuppressWarnings("unchecked")
  83.     public static <E> Closure<E> switchClosure(final Predicate<? super E>[] predicates,
  84.                                                final Closure<? super E>[] closures,
  85.                                                final Closure<? super E> defaultClosure) {
  86.         FunctorUtils.validate(predicates);
  87.         FunctorUtils.validate(closures);
  88.         if (predicates.length != closures.length) {
  89.             throw new IllegalArgumentException("The predicate and closure arrays must be the same size");
  90.         }
  91.         if (predicates.length == 0) {
  92.             return (Closure<E>) (defaultClosure == null ? NOPClosure.<E>nopClosure() : defaultClosure);
  93.         }
  94.         return new SwitchClosure<>(predicates, closures, defaultClosure);
  95.     }
  96.     /** The tests to consider */
  97.     private final Predicate<? super T>[] iPredicates;

  98.     /** The matching closures to call */
  99.     private final Closure<? super T>[] iClosures;

  100.     /** The default closure to call if no tests match */
  101.     private final Closure<? super T> iDefault;

  102.     /**
  103.      * Hidden constructor for the use by the static factory methods.
  104.      *
  105.      * @param clone  if {@code true} the input arguments will be cloned
  106.      * @param predicates  array of predicates, no nulls
  107.      * @param closures  matching array of closures, no nulls
  108.      * @param defaultClosure  the closure to use if no match, null means nop
  109.      */
  110.     private SwitchClosure(final boolean clone, final Predicate<? super T>[] predicates,
  111.                           final Closure<? super T>[] closures, final Closure<? super T> defaultClosure) {
  112.         iPredicates = clone ? FunctorUtils.copy(predicates) : predicates;
  113.         iClosures = clone ? FunctorUtils.copy(closures) : closures;
  114.         iDefault = defaultClosure == null ? NOPClosure.<T>nopClosure() : defaultClosure;
  115.     }

  116.     /**
  117.      * Constructor that performs no validation.
  118.      * Use {@code switchClosure} if you want that.
  119.      *
  120.      * @param predicates  array of predicates, cloned, no nulls
  121.      * @param closures  matching array of closures, cloned, no nulls
  122.      * @param defaultClosure  the closure to use if no match, null means nop
  123.      */
  124.     public SwitchClosure(final Predicate<? super T>[] predicates, final Closure<? super T>[] closures,
  125.                          final Closure<? super T> defaultClosure) {
  126.         this(true, predicates, closures, defaultClosure);
  127.     }

  128.     /**
  129.      * Executes the closure whose matching predicate returns true
  130.      *
  131.      * @param input  the input object
  132.      */
  133.     @Override
  134.     public void execute(final T input) {
  135.         for (int i = 0; i < iPredicates.length; i++) {
  136.             if (iPredicates[i].test(input)) {
  137.                 iClosures[i].accept(input);
  138.                 return;
  139.             }
  140.         }
  141.         iDefault.accept(input);
  142.     }

  143.     /**
  144.      * Gets the closures.
  145.      *
  146.      * @return a copy of the closures
  147.      * @since 3.1
  148.      */
  149.     public Closure<? super T>[] getClosures() {
  150.         return FunctorUtils.copy(iClosures);
  151.     }

  152.     /**
  153.      * Gets the default closure.
  154.      *
  155.      * @return the default closure
  156.      * @since 3.1
  157.      */
  158.     public Closure<? super T> getDefaultClosure() {
  159.         return iDefault;
  160.     }

  161.     /**
  162.      * Gets the predicates.
  163.      *
  164.      * @return a copy of the predicates
  165.      * @since 3.1
  166.      */
  167.     public Predicate<? super T>[] getPredicates() {
  168.         return FunctorUtils.copy(iPredicates);
  169.     }

  170. }