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