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}