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   * @since 3.0
31   */
32  public class SwitchTransformer<I, O> implements Transformer<I, O>, Serializable {
33  
34      /** Serial version UID */
35      private static final long serialVersionUID = -6404460890903469332L;
36  
37      /**
38       * Create a new Transformer that calls one of the transformers depending
39       * on the predicates.
40       * <p>
41       * The Map consists of Predicate keys and Transformer values. A transformer
42       * is called if its matching predicate returns true. Each predicate is evaluated
43       * until one returns true. If no predicates evaluate to true, the default
44       * transformer is called. The default transformer is set in the map with a
45       * null key. The ordering is that of the iterator() method on the entryset
46       * collection of the map.
47       *
48       * @param <I>  the input type
49       * @param <O>  the output type
50       * @param map  a map of predicates to transformers
51       * @return the {@code switch} transformer
52       * @throws NullPointerException if the map is null
53       * @throws NullPointerException if any transformer in the map is null
54       * @throws ClassCastException  if the map elements are of the wrong type
55       */
56      @SuppressWarnings("unchecked")
57      public static <I, O> Transformer<I, O> switchTransformer(
58              final Map<? extends Predicate<? super I>, ? extends Transformer<? super I, ? extends O>> map) {
59  
60          Objects.requireNonNull(map, "map");
61          if (map.isEmpty()) {
62              return ConstantTransformer.<I, O>nullTransformer();
63          }
64          // convert to array like this to guarantee iterator() ordering
65          final Transformer<? super I, ? extends O> defaultTransformer = map.remove(null);
66          final int size = map.size();
67          if (size == 0) {
68              return (Transformer<I, O>) (defaultTransformer == null ? ConstantTransformer.<I, O>nullTransformer() :
69                                                                       defaultTransformer);
70          }
71          final Transformer<? super I, ? extends O>[] transformers = new Transformer[size];
72          final Predicate<? super I>[] preds = new Predicate[size];
73          int i = 0;
74          for (final Map.Entry<? extends Predicate<? super I>,
75                               ? extends Transformer<? super I, ? extends O>> entry : map.entrySet()) {
76              preds[i] = entry.getKey();
77              transformers[i] = entry.getValue();
78              i++;
79          }
80          return new SwitchTransformer<>(false, preds, transformers, defaultTransformer);
81      }
82      /**
83       * Factory method that performs validation and copies the parameter arrays.
84       *
85       * @param <I>  the input type
86       * @param <O>  the output type
87       * @param predicates  array of predicates, cloned, no nulls
88       * @param transformers  matching array of transformers, cloned, no nulls
89       * @param defaultTransformer  the transformer to use if no match, null means return null
90       * @return the {@code chained} transformer
91       * @throws NullPointerException if either array is null
92       * @throws NullPointerException if any element in the arrays is null
93       * @throws IllegalArgumentException if the arrays have different sizes
94       */
95      @SuppressWarnings("unchecked")
96      public static <I, O> Transformer<I, O> switchTransformer(final Predicate<? super I>[] predicates,
97              final Transformer<? super I, ? extends O>[] transformers,
98              final Transformer<? super I, ? extends O> defaultTransformer) {
99          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 }