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.functors;
018
019import java.io.Serializable;
020import java.util.Map;
021import java.util.Objects;
022
023import org.apache.commons.collections4.Closure;
024import org.apache.commons.collections4.Predicate;
025
026/**
027 * Closure implementation calls the closure whose predicate returns true,
028 * like a switch statement.
029 *
030 * @param <T> the type of the input to the operation.
031 * @since 3.0
032 */
033public class SwitchClosure<T> implements Closure<T>, Serializable {
034
035    /** Serial version UID */
036    private static final long serialVersionUID = 3518477308466486130L;
037
038    /**
039     * Create a new Closure that calls one of the closures depending
040     * on the predicates.
041     * <p>
042     * The Map consists of Predicate keys and Closure values. A closure
043     * is called if its matching predicate returns true. Each predicate is evaluated
044     * until one returns true. If no predicates evaluate to true, the default
045     * closure is called. The default closure is set in the map with a
046     * null key. The ordering is that of the iterator() method on the entryset
047     * collection of the map.
048     *
049     * @param <E> the type that the closure acts on
050     * @param predicatesAndClosures  a map of predicates to closures
051     * @return the {@code switch} closure
052     * @throws NullPointerException if the map is null
053     * @throws NullPointerException if any closure in the map is null
054     * @throws ClassCastException  if the map elements are of the wrong type
055     */
056    @SuppressWarnings("unchecked")
057    public static <E> Closure<E> switchClosure(final Map<Predicate<E>, Closure<E>> predicatesAndClosures) {
058        Objects.requireNonNull(predicatesAndClosures, "predicatesAndClosures");
059        // convert to array like this to guarantee iterator() ordering
060        final Closure<? super E> defaultClosure = predicatesAndClosures.remove(null);
061        final int size = predicatesAndClosures.size();
062        if (size == 0) {
063            return (Closure<E>) (defaultClosure == null ? NOPClosure.<E>nopClosure() : defaultClosure);
064        }
065        final Closure<E>[] closures = new Closure[size];
066        final Predicate<E>[] preds = new Predicate[size];
067        int i = 0;
068        for (final Map.Entry<Predicate<E>, Closure<E>> entry : predicatesAndClosures.entrySet()) {
069            preds[i] = entry.getKey();
070            closures[i] = entry.getValue();
071            i++;
072        }
073        return new SwitchClosure<>(false, preds, closures, defaultClosure);
074    }
075    /**
076     * Factory method that performs validation and copies the parameter arrays.
077     *
078     * @param <E> the type that the closure acts on
079     * @param predicates  array of predicates, cloned, no nulls
080     * @param closures  matching array of closures, cloned, no nulls
081     * @param defaultClosure  the closure to use if no match, null means nop
082     * @return the {@code chained} closure
083     * @throws NullPointerException if array is null
084     * @throws NullPointerException if any element in the array is null
085     * @throws IllegalArgumentException if the array lengths of predicates and closures do not match
086     */
087    @SuppressWarnings("unchecked")
088    public static <E> Closure<E> switchClosure(final Predicate<? super E>[] predicates,
089                                               final Closure<? super E>[] closures,
090                                               final Closure<? super E> defaultClosure) {
091        FunctorUtils.validate(predicates);
092        FunctorUtils.validate(closures);
093        if (predicates.length != closures.length) {
094            throw new IllegalArgumentException("The predicate and closure arrays must be the same size");
095        }
096        if (predicates.length == 0) {
097            return (Closure<E>) (defaultClosure == null ? NOPClosure.<E>nopClosure() : defaultClosure);
098        }
099        return new SwitchClosure<>(predicates, closures, defaultClosure);
100    }
101    /** The tests to consider */
102    private final Predicate<? super T>[] iPredicates;
103
104    /** The matching closures to call */
105    private final Closure<? super T>[] iClosures;
106
107    /** The default closure to call if no tests match */
108    private final Closure<? super T> iDefault;
109
110    /**
111     * Hidden constructor for the use by the static factory methods.
112     *
113     * @param clone  if {@code true} the input arguments will be cloned
114     * @param predicates  array of predicates, no nulls
115     * @param closures  matching array of closures, no nulls
116     * @param defaultClosure  the closure to use if no match, null means nop
117     */
118    private SwitchClosure(final boolean clone, final Predicate<? super T>[] predicates,
119                          final Closure<? super T>[] closures, final Closure<? super T> defaultClosure) {
120        iPredicates = clone ? FunctorUtils.copy(predicates) : predicates;
121        iClosures = clone ? FunctorUtils.copy(closures) : closures;
122        iDefault = defaultClosure == null ? NOPClosure.<T>nopClosure() : defaultClosure;
123    }
124
125    /**
126     * Constructor that performs no validation.
127     * Use {@code switchClosure} if you want that.
128     *
129     * @param predicates  array of predicates, cloned, no nulls
130     * @param closures  matching array of closures, cloned, no nulls
131     * @param defaultClosure  the closure to use if no match, null means nop
132     */
133    public SwitchClosure(final Predicate<? super T>[] predicates, final Closure<? super T>[] closures,
134                         final Closure<? super T> defaultClosure) {
135        this(true, predicates, closures, defaultClosure);
136    }
137
138    /**
139     * Executes the closure whose matching predicate returns true
140     *
141     * @param input  the input object
142     */
143    @Override
144    public void execute(final T input) {
145        for (int i = 0; i < iPredicates.length; i++) {
146            if (iPredicates[i].test(input)) {
147                iClosures[i].accept(input);
148                return;
149            }
150        }
151        iDefault.accept(input);
152    }
153
154    /**
155     * Gets the closures.
156     *
157     * @return a copy of the closures
158     * @since 3.1
159     */
160    public Closure<? super T>[] getClosures() {
161        return FunctorUtils.copy(iClosures);
162    }
163
164    /**
165     * Gets the default closure.
166     *
167     * @return the default closure
168     * @since 3.1
169     */
170    public Closure<? super T> getDefaultClosure() {
171        return iDefault;
172    }
173
174    /**
175     * Gets the predicates.
176     *
177     * @return a copy of the predicates
178     * @since 3.1
179     */
180    public Predicate<? super T>[] getPredicates() {
181        return FunctorUtils.copy(iPredicates);
182    }
183
184}