1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.collections4.functors;
18
19 import java.io.Serializable;
20 import java.util.Map;
21 import java.util.Objects;
22
23 import org.apache.commons.collections4.Predicate;
24 import org.apache.commons.collections4.Transformer;
25
26 /**
27 * Transformer implementation calls the transformer whose predicate returns true,
28 * like a switch statement.
29 *
30 * @param <T> the type of the input to the function.
31 * @param <R> the type of the result of the function.
32 * @since 3.0
33 */
34 public class SwitchTransformer<T, R> implements Transformer<T, R>, Serializable {
35
36 /** Serial version UID */
37 private static final long serialVersionUID = -6404460890903469332L;
38
39 /**
40 * Create a new Transformer that calls one of the transformers depending
41 * on the predicates.
42 * <p>
43 * The Map consists of Predicate keys and Transformer values. A transformer
44 * is called if its matching predicate returns true. Each predicate is evaluated
45 * until one returns true. If no predicates evaluate to true, the default
46 * transformer is called. The default transformer is set in the map with a
47 * null key. The ordering is that of the iterator() method on the entryset
48 * collection of the map.
49 *
50 * @param <I> the input type
51 * @param <O> the output type
52 * @param map a map of predicates to transformers
53 * @return the {@code switch} transformer
54 * @throws NullPointerException if the map is null
55 * @throws NullPointerException if any transformer in the map is null
56 * @throws ClassCastException if the map elements are of the wrong type
57 */
58 @SuppressWarnings("unchecked")
59 public static <I, O> Transformer<I, O> switchTransformer(
60 final Map<? extends Predicate<? super I>, ? extends Transformer<? super I, ? extends O>> map) {
61
62 Objects.requireNonNull(map, "map");
63 if (map.isEmpty()) {
64 return ConstantTransformer.<I, O>nullTransformer();
65 }
66 // convert to array like this to guarantee iterator() ordering
67 final Transformer<? super I, ? extends O> defaultTransformer = map.remove(null);
68 final int size = map.size();
69 if (size == 0) {
70 return (Transformer<I, O>) (defaultTransformer == null ? ConstantTransformer.<I, O>nullTransformer() :
71 defaultTransformer);
72 }
73 final Transformer<? super I, ? extends O>[] transformers = new Transformer[size];
74 final Predicate<? super I>[] preds = new Predicate[size];
75 int i = 0;
76 for (final Map.Entry<? extends Predicate<? super I>,
77 ? extends Transformer<? super I, ? extends O>> entry : map.entrySet()) {
78 preds[i] = entry.getKey();
79 transformers[i] = entry.getValue();
80 i++;
81 }
82 return new SwitchTransformer<>(false, preds, transformers, defaultTransformer);
83 }
84 /**
85 * Factory method that performs validation and copies the parameter arrays.
86 *
87 * @param <I> the input type
88 * @param <O> the output type
89 * @param predicates array of predicates, cloned, no nulls
90 * @param transformers matching array of transformers, cloned, no nulls
91 * @param defaultTransformer the transformer to use if no match, null means return null
92 * @return the {@code chained} transformer
93 * @throws NullPointerException if either array is null
94 * @throws NullPointerException if any element in the arrays is null
95 * @throws IllegalArgumentException if the arrays have different sizes
96 */
97 @SuppressWarnings("unchecked")
98 public static <I, O> Transformer<I, O> switchTransformer(final Predicate<? super I>[] predicates,
99 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 }