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