View Javadoc
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 }