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.Transformer;
19  
20  import java.io.Serializable;
21  
22  /***
23   * <code>Transformer</code> implementation that chains two specified
24   * <code>Transformer</code>s together. The principal restriction on the chaining
25   * together of two <code>Transformer</code>s into a chain is that the generic
26   * output type of the first <code>Transformer</code> must match the generic
27   * input type of the second <code>Transformer</code>. For example:
28   * <pre>
29   * Transformer&lt;A, B&gt; t1;
30   * Transformer&lt;B, C&gt; t2;
31   * ...
32   * Transformer&lt;A, C&gt; chained = ChainedTransformer.getInstance(t1, t2);
33   * </pre>
34   * The <code>transform()</code> method of a <code>ChainedTransformer</code>
35   * works by transforming an input object using the first
36   * <code>Transformer</code>, resulting in an intermediate object. This is then
37   * transformed by the second <code>Transformer</code>, resulting in the returned
38   * output object.
39   * <p/>
40   * The methods {@link #prepend} and {@link #append} can be used to add
41   * additional <code>Transformer</code>s to the chain, allowing multiple
42   * <code>Transformer</code>s to be chained together in a typesafe manner. In
43   * this way, a tree of <code>Transformers</code> can be built up, whereby the
44   * leaves of the tree form a typesafe chain of <code>Transformer</code>s The
45   * input type of the first leaf <code>Transformer</code> is <code>I</code>, and
46   * the output type of the last leaf <code>Transformer</code> is <code>O</code>.
47   * The transform process takes an input object, transforms it using the first
48   * leaf <code>Transformer</code>, and passes the result on to the next leaf.
49   * This process repeats along the chain of leaves, until the last leaf
50   * <code>Transformer</code> in the chain produces the final output object.
51   * <p/>
52   * Here's an example, showing the use of the static factory method, and both the
53   * <code>prepend</code> and <code>append</code> methods:
54   * <pre>
55   * Transformer&lt;A, B&gt; t1;
56   * Transformer&lt;B, C&gt; t2;
57   * Transformer&lt;C, D&gt; t3;
58   * Transformer&lt;D, E&gt; t4;
59   * ...
60   * Transformer&lt;A, E&gt; chained = ChainedTransformer.getInstance(t2, t3)
61   *     .append(t4).prepend(t1);
62   *
63   * @author Chris Lambrou
64   * @since Collections15 1.0
65   */
66  public class ChainedTransformer <I, M, O> implements Transformer<I, O>, Serializable
67  {
68  
69      static final long serialVersionUID = -8498145290061843155L;
70  
71      /***
72       * The first <code>Transformer</code> that transforms the input object to
73       * the intermediate object.
74       */
75      private Transformer<? super I, ? extends M> firstTransformer;
76  
77      /***
78       * The second <code>Transformer</code> that transforms the intermediate
79       * object to the output object.
80       */
81      private Transformer<? super M, ? extends O> secondTransformer;
82  
83      /***
84       * Returns a new <code>ChainedTransformer</code> instance that chains
85       * together the two specified <code>Transformer</code>s.
86       *
87       * @param firstTransformer  The first <code>Transformer</code> in the
88       *                          chain.
89       * @param secondTransformer The second <code>Transformer</code> in the
90       *                          chain.
91       *
92       * @return A new <code>ChainedTransformer</code> instance that chains
93       *         together the two specified <code>Transformer</code>s.
94       *
95       * @throws IllegalArgumentException Thrown if either <code>Transformer</code>
96       *                                  is <code>null</code>.
97       */
98      public static <I, M, O> ChainedTransformer<I, M, O> getInstance(Transformer<? super I, ? extends M> firstTransformer,
99                                                                      Transformer<? super M, ? extends O> secondTransformer)
100     {
101         return new ChainedTransformer<I, M, O>(firstTransformer, secondTransformer);
102     }
103 
104     /***
105      * Creates a new instance that chains together the two specified
106      * <code>Transformer</code>s.
107      *
108      * @param firstTransformer  The first <code>Transformer</code> in the
109      *                          chain.
110      * @param secondTransformer The second <code>Transformer</code> in the
111      *                          chain.
112      *
113      * @throws IllegalArgumentException Thrown if either <code>Transformer</code>
114      *                                  is <code>null</code>.
115      */
116     protected ChainedTransformer(Transformer<? super I, ? extends M> firstTransformer,
117                                  Transformer<? super M, ? extends O> secondTransformer)
118     {
119         if (firstTransformer == null) {
120             throw new IllegalArgumentException("null first transformer");
121         }
122         if (secondTransformer == null) {
123             throw new IllegalArgumentException("null second transformer");
124         }
125         this.firstTransformer = firstTransformer;
126         this.secondTransformer = secondTransformer;
127     }
128 
129     /***
130      * Appends a specified <code>Transformer</code> to the end of the chain,
131      * resulting in a new <code>ChainedTransformer</code> that transforms an
132      * input object through the existing <code>Transformer</code>s in this
133      * chain, and then transforms the result through the specified
134      * <code>Transformer</code>.
135      *
136      * @param transformer The <code>Transformer</code> to append to the chain.
137      *
138      * @return A new <code>ChainedTransformer</code> that transforms an input
139      *         object through the existing <code>Transformer</code>s in this
140      *         chain, and then transforms the result through the specified
141      *         <code>Transformer</code>.
142      *
143      * @throws IllegalArgumentException Thrown if the specified <code>Transformer</code>
144      *                                  is <code>null</code>.
145      */
146     public <T> ChainedTransformer<I, O, T> append(Transformer<? super O, T> transformer)
147     {
148         return new ChainedTransformer<I, O, T>(this, transformer);
149     }
150 
151     /***
152      * Prepends a specified <code>Transformer</code> to the start of the chain,
153      * resulting in a new <code>ChainedTransformer</code> that transforms an
154      * input object through the specified <code>Transformer</code>, and then
155      * transforms the result through the existing <code>Transformer</code>s in
156      * this chain.
157      *
158      * @param transformer The <code>Transformer</code> to prepend to the chain.
159      *
160      * @return A new <code>ChainedTransformer</code> that transforms an input
161      *         object through the specified <code>Transformer</code>, and then
162      *         transforms the result through the existing <code>Transformer</code>s
163      *         in this chain.
164      *
165      * @throws IllegalArgumentException Thrown if the specified <code>Transformer</code>
166      *                                  is <code>null</code>.
167      */
168     public <T> ChainedTransformer<T, I, O> prepend(Transformer<T, ? extends I> transformer)
169     {
170         return new ChainedTransformer<T, I, O>(transformer, this);
171     }
172 
173     /***
174      * Transforms the input object by transforming it using the first
175      * <code>Transformer</code>, and then transforming the result using the
176      * second <code>Transformer</code>, returning the resulting output object.
177      *
178      * @param input The object to be transformed.
179      *
180      * @return The transformed object
181      */
182     public O transform(I input)
183     {
184         return secondTransformer.transform(firstTransformer.transform(input));
185     }
186 
187 }