View Javadoc

1   /*
2    *  Copyright 2001-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package net.sf.collections15.functors.transformer;
17  
18  import net.sf.collections15.MultplePredicateDecorator;
19  import net.sf.collections15.Predicate;
20  import net.sf.collections15.Transformer;
21  
22  import java.io.Serializable;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.List;
26  import java.util.Map;
27  
28  /***
29   * <code>Transformer</code> implementation that defers to the
30   * <code>Transformer</code> whose corresponding <code>Predicate</code> evaluates
31   * to <code>true</code>, like a switch statement.
32   *
33   * @author Chris Lambrou
34   * @since Collections15 1.0
35   */
36  public class SwitchTransformer <I, O> implements Transformer<I, O>, MultplePredicateDecorator<I>, Serializable
37  {
38  
39      static final long serialVersionUID = 1208341652199297260L;
40  
41      /***
42       * The <code>Predicate</code> that define the tests to evaluate.
43       */
44      private List<Predicate<? super I>> predicates;
45  
46      /***
47       * The <code>Transformer</code>s that implement each case.
48       */
49      private List<Transformer<? super I, ? extends O>> transformers;
50  
51      /***
52       * The default <code>Transformer</code> to use if no tests match. May be
53       * <code>null</code>, in which case an <code>IllegalArgumentException</code>
54       * is thrown if this <code>Transformer</code> attempts to transform an input
55       * object which doesn't satisfy any of the cases.
56       */
57      private Transformer<? super I, ? extends O> defaultTransformer;
58  
59      /***
60       * Creates a new instance that uses the specified map of
61       * <code>Predicates</code> and <code>Transformer</code>s.
62       *
63       * @param predicatesAndTransformers A map of <code>Predicate</code>s to
64       *                                  <code>Transformer</code>s. Each non-null
65       *                                  <code>Predicate</code> key defines a
66       *                                  single switch case, with the
67       *                                  corresponding <code>Transformer</code>
68       *                                  being used to transform an input object
69       *                                  if it matches the <code>Predicate</code>.
70       *                                  <p/>
71       *                                  <code>Predicate</code>s are evaluated in
72       *                                  the iteration order of the map's key
73       *                                  set, with the exception of the
74       *                                  <code>null Predicate</code> key. If a
75       *                                  <code>null Predicate</code> key is
76       *                                  specified in the set, it's corresponding
77       *                                  <code>Transformer</code> is treated as
78       *                                  the default case, used to transform an
79       *                                  input object if it doesn't satisfy any
80       *                                  of the non-<code>null Predicate</code>
81       *                                  cases.
82       *                                  <p/>
83       *                                  None of the <code>Transformer</code>
84       *                                  values may be <code>null</code>, and
85       *                                  only one <code>Predicate</code> may be
86       *                                  <code>null</code>.
87       *                                  <p/>
88       *                                  <b>Note that the map is not modified by
89       *                                  this method</b>.
90       *
91       * @throws IllegalArgumentException Thrown if the map is <code>null</code>.
92       * @throws IllegalArgumentException Thrown if any <code>Transformer</code>
93       *                                  in the map is <code>null</code>.
94       */
95      private SwitchTransformer(Map<Predicate<I>, Transformer<I, O>> predicatesAndTransformers)
96      {
97          if (predicatesAndTransformers == null) {
98              throw new IllegalArgumentException("The predicate and transformer map must not be null");
99          }
100 
101         Transformer<I, O> defaultTransformer = null;
102         int size = predicatesAndTransformers.size();
103         predicates = new ArrayList<Predicate<? super I>>(size);
104         transformers = new ArrayList<Transformer<? super I, ? extends O>>(size);
105         for (Map.Entry<? extends Predicate<I>, ? extends Transformer<I, O>> entry : predicatesAndTransformers.entrySet()) {
106             Predicate<I> predicate = entry.getKey();
107             Transformer<I, O> transformer = entry.getValue();
108             if (transformer == null) {
109                 throw new NullPointerException("null transformer specified");
110             }
111             if (predicate == null) {
112                 if (defaultTransformer == null) {
113                     defaultTransformer = transformer;
114                     continue;
115                 }
116                 else {
117                     throw new NullPointerException("multiple null predicates specified");
118                 }
119             }
120             predicates.add(predicate);
121             transformers.add(transformer);
122         }
123         this.defaultTransformer = defaultTransformer;
124     }
125 
126     /***
127      * Creates a new instance that uses the specified map of
128      * <code>Predicates</code> and <code>Transformer</code>s.
129      *
130      * @param predicatesAndTransformers A map of <code>Predicate</code>s to
131      *                                  <code>Transformer</code>s. Each non-null
132      *                                  <code>Predicate</code> key defines a
133      *                                  single switch case, with the
134      *                                  corresponding <code>Transformer</code>
135      *                                  being used to transform an input object
136      *                                  if it matches the <code>Predicate</code>.
137      *                                  <p/>
138      *                                  <code>Predicate</code>s are evaluated in
139      *                                  the iteration order of the map's key
140      *                                  set, with the exception of the
141      *                                  <code>null Predicate</code> key. If a
142      *                                  <code>null Predicate</code> key is
143      *                                  specified in the set, it's corresponding
144      *                                  <code>Transformer</code> is treated as
145      *                                  the default case, used to transform an
146      *                                  input object if it doesn't satisfy any
147      *                                  of the non-<code>null Predicate</code>
148      *                                  cases.
149      *                                  <p/>
150      *                                  None of the <code>Transformer</code>
151      *                                  values may be <code>null</code>, and
152      *                                  only one <code>Predicate</code> may be
153      *                                  <code>null</code>.
154      *                                  <p/>
155      *                                  <b>Note that the map is not modified by
156      *                                  this method</b>.
157      *
158      * @return A new instance that uses the specified map of
159      *         <code>Predicates</code> and <code>Transformer</code>s.
160      *
161      * @throws IllegalArgumentException Thrown if the map is <code>null</code>.
162      * @throws IllegalArgumentException Thrown if any <code>Transformer</code>
163      *                                  in the map is <code>null</code>.
164      */
165     public static <I, O> Transformer<I, O> getInstance(Map<Predicate<I>, Transformer<I, O>> predicatesAndTransformers)
166     {
167         if (predicatesAndTransformers == null) {
168             throw new IllegalArgumentException("The predicate and transformer map must not be null");
169         }
170         int size = predicatesAndTransformers.size();
171         if (size == 1 && predicatesAndTransformers.containsKey(null)) {
172             Transformer<I, O> defaultTransformer = predicatesAndTransformers.get(null);
173             if (defaultTransformer == null) {
174                 throw new IllegalArgumentException("null default transformer specified");
175             }
176             return defaultTransformer;
177         }
178         return new SwitchTransformer<I, O>(predicatesAndTransformers);
179     }
180 
181     /***
182      * Transforms the input to result by delegating to the
183      * <code>Transformer</code> whose matching <code>Predicate</code> evaluates
184      * to <code>true</code>. If no <code>Predicate</code> evaluates to
185      * <code>true</code>, the default <code>Transformer</code> is used. If there
186      * is no default <code>Transformer</code>, an <code>IllegalArgumentException</code>
187      * is thrown.
188      *
189      * @param input The input object to transform.
190      *
191      * @return The transformed result
192      *
193      * @throws IllegalArgumentException Thrown if the input doesn't match any of
194      *                                  the cases, and no default <code>Transformer</code>
195      *                                  is defined.
196      */
197     public O transform(I input)
198     {
199         int size = predicates.size();
200         for (int i = 0; i < size; i++) {
201             Predicate<? super I> predicate = predicates.get(i);
202             if (predicate.evaluate(input)) {
203                 return transformers.get(i).transform(input);
204             }
205         }
206         if (defaultTransformer == null) {
207             throw new IllegalArgumentException("input doesn't match any cases, and no default is defined");
208         }
209         return defaultTransformer.transform(input);
210     }
211 
212     /***
213      * Returns a list of the <code>Predicate</code>s that define each case.
214      *
215      * @return A list of the decorated <code>Predicate</code>s that define each
216      *         case.. The returned list is unmodifiable.
217      */
218     public List<Predicate<? super I>> getDecoratedPredicates()
219     {
220         return Collections.unmodifiableList(predicates);
221     }
222 
223     /***
224      * Returns the <code>Transformer</code>s used by each case.
225      *
226      * @return A list of <code>Transformer</code>s used by this
227      *         <code>SwitchTransformer</code>. There is a one to one mapping
228      *         between the <code>Transformer</code>s in the list and the
229      *         <code>Predicate</code>s in the list returned by {@link
230      *         #getDecoratedPredicates}.
231      *
232      * @see #getDefaultTransformer().
233      */
234     public List<Transformer<? super I, ? extends O>> getTransformers()
235     {
236         return Collections.unmodifiableList(transformers);
237     }
238 
239     /***
240      * Returns the <code>Transformer</code> used in the default case.
241      *
242      * @return The <code>Transformer</code> used to transform an input object if
243      *         it doesn't match any of the <code>Predicate</code>s returned by
244      *         {@link #getDecoratedPredicates} - <code>null</code> if no default
245      *         is defined.
246      *
247      * @see #getDecoratedPredicates()
248      * @see #getTransformers()
249      * @since Collections15 1.0
250      */
251     public Transformer<? super I, ? extends O> getDefaultTransformer()
252     {
253         return defaultTransformer;
254     }
255 }