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