1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.collections15.functors.closure;
17
18 import net.sf.collections15.Closure;
19 import net.sf.collections15.Predicate;
20 import net.sf.collections15.functors.FunctorException;
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 /***
30 * <code>Closure</code> implementation that calls the <code>Closure</code> whose
31 * <code>Predicate</code> returns <code>true</code>, like a switch statement.
32 *
33 * @author Stephen Colebourne
34 * @author Chris Lambrou (port to Java 5.0)
35 * @since Collections15 1.0
36 */
37 public class SwitchClosure <E> implements Closure<E>, Serializable
38 {
39
40 static final long serialVersionUID = 7882246541467547697L;
41
42 /***
43 * The cases to consider.
44 */
45 private final List<Predicate<? super E>> predicates;
46
47 /***
48 * The matching <code>Closure</code>s to execute.
49 */
50 private final List<Closure<? super E>> closures;
51
52 /***
53 * The default <code>Closure</code> to execute if no tests match.
54 */
55 private final Closure<? super E> defaultClosure;
56
57 /***
58 * Create a new <code>Closure</code> that calls one of a number of
59 * <code>Closures</code> in a map depending on whether or not the input
60 * object satisfies any of the <code>Predicates</code> in the map.
61 * <p/>
62 * The map consists of <code>Predicate</code> keys and <code>Closure</code>
63 * values. A <code>Closure</code> is executed on the input object if the
64 * input matches its corresponding <code>Predicate</code>. A default
65 * <code>Closure</code> can be specified in the map with a <code>null</code>
66 * key, and is executed on an input object if it doesn't satisfy any of the
67 * other non-null <code>Predicate</code> keys.
68 *
69 * @param predicatesAndClosures A map of <code>Predicate</code>s to
70 * <code>Closure</code>s. An input object is
71 * evaluated against the non-null
72 * <code>Predicate</code> keys, in the order
73 * defined by the entry set's iterator. For the
74 * first <code>Predicate</code> to evaluate to
75 * <code>true</code> against the key, the
76 * corresponding <code>Closure</code> is
77 * executed.
78 * <p/>
79 * If all of the (non-null) <code>Predicate</code>s
80 * evaluate to <code>false</code>, and a
81 * default <code>Closure</code> is defined (with a
82 * <code>null Predicate</code>), the default
83 * <code>Closure</code> is executed against the key, othersize an
84 * <code>IllegalArgumentException</code> will
85 * be thrown.
86 * @return The <code>switch</code> closure based on the contents of the
87 * specified map.
88 * @throws IllegalArgumentException If the map is <code>null</code>.
89 * @throws IllegalArgumentException If any <code>Closure</code> in the map
90 * is <code>null</code>.
91 */
92 public static <T> Closure<T> getInstance(Map<Predicate<? super T>, Closure<? super T>> predicatesAndClosures)
93 {
94 return new SwitchClosure<T>(predicatesAndClosures);
95 }
96
97 /***
98 * Creates a new <code>Closure</code> that calls one of a number of
99 * <code>Closures</code> in a map depending on whether or not the input
100 * object satisfies any of the <code>Predicates</code> in the map.
101 * <p/>
102 * The map consists of <code>Predicate</code> keys and <code>Closure</code>
103 * values. A <code>Closure</code> is executed on the input object if the
104 * input matches its corresponding <code>Predicate</code>. A default
105 * <code>Closure</code> can be specified in the map with a <code>null</code>
106 * key, and is executed on an input object if it doesn't satisfy any of the
107 * other non-null <code>Predicate</code> keys.
108 *
109 * @param predicatesAndClosures A map of <code>Predicate</code>s to
110 * <code>Closure</code>s. An input object is
111 * evaluated against the non-null
112 * <code>Predicate</code> keys, in the order
113 * defined by the entry set's iterator. For the
114 * first <code>Predicate</code> to evaluate to
115 * <code>true</code> against the key, the
116 * corresponding <code>Closure</code> is
117 * executed.
118 * <p/>
119 * If none of the non-null <code>Predicate</code>s
120 * evaluates to <code>true</code>, and a
121 * default <code>Closure</code> is defined, it
122 * is executed against the key, othersize an
123 * <code>IllegalArgumentException</code> will
124 * be thrown.
125 * @throws IllegalArgumentException If the map is <code>null</code>.
126 * @throws IllegalArgumentException If any <code>Closure</code> in the map
127 * is <code>null</code>.
128 */
129 protected SwitchClosure(Map<Predicate<? super E>, Closure<? super E>> predicatesAndClosures)
130 {
131 if (predicatesAndClosures == null) {
132 throw new IllegalArgumentException("The predicate and closure map must not be null");
133 }
134 int size = predicatesAndClosures.size();
135 predicates = new ArrayList<Predicate<? super E>>(size);
136 closures = new ArrayList<Closure<? super E>>(size);
137 Closure<? super E> defaultClosure = ExceptionClosure.getInstance(
138 "Unknown case - SwitchClosure cannot execute the specified target object");
139 for (Map.Entry<Predicate<? super E>, Closure<? super E>> entry : predicatesAndClosures.entrySet()) {
140 Predicate<? super E> predicate = entry.getKey();
141 Closure<? super E> closure = entry.getValue();
142 if (closure == null) {
143 throw new IllegalArgumentException("null closure found in predicates and closures map");
144 }
145 if (predicate == null) {
146 defaultClosure = closure;
147 continue;
148 }
149 predicates.add(predicate);
150 closures.add(closure);
151 }
152 this.defaultClosure = defaultClosure;
153 }
154
155 /***
156 * Executes the <code>Closure</code> whose matching <code>Predicate</code>
157 * evaluates to <code>true</code>.
158 *
159 * @param input The input object to act upon.
160 * @throws FunctorException Thrown if the input doesn't match any of the
161 * <code>Predicate</code> cases, and no default
162 * <code>Closure</code> is defined.
163 */
164 public void execute(E input)
165 {
166 int size = predicates.size();
167 for (int i = 0; i < size; i++) {
168 if (predicates.get(i).evaluate(input)) {
169 closures.get(i).execute(input);
170 return;
171 }
172 }
173 defaultClosure.execute(input);
174 }
175
176 /***
177 * Returns the <code>Predicate</code>s that define the case conditions for
178 * this <code>SwitchClosure</code> - see {@link #getClosures()}.
179 *
180 * @return A list of the <code>Predicate</code>s used by this
181 * <code>SwitchClosure</code> to determine which
182 * <code>Closure</code> to apply to an input object. The order of
183 * the list represents the order in which the <code>Predicate</code>s
184 * are evaluated, and there is a one to one mapping between the
185 * <code>Predicates</code> in the list and the <code>Closure</code>s
186 * in the list returned by {@link #getClosures()}.
187 * @since Collections15 1.0
188 */
189 public List<Predicate<? super E>> getPredicates()
190 {
191 return Collections.unmodifiableList(predicates);
192 }
193
194 /***
195 * Returns the <code>Closure</code>s that are executed by each case for this
196 * <code>SwitchClosure</code> - see {@link #getPredicates()}.
197 *
198 * @return A list of the <code>Closure</code>s applied to an input object
199 * used by this <code>SwitchClosure</code>. There is a one to one
200 * mapping between the <code>Closure</code>s in this list and the
201 * <code>Predicate</code>s in the list returned by {@link
202 * #getPredicates()}.
203 * @see #getDefaultClosure()
204 * @since Collections15 1.0
205 */
206 public List<Closure<? super E>> getClosures()
207 {
208 return Collections.unmodifiableList(closures);
209 }
210
211 /***
212 * Returns the default <code>Closure</code>.
213 *
214 * @return The default <code>Closure</code> applied to an input object if it
215 * doesn't satisfy any of the cases defined for this
216 * <code>SwitchClosure</code>. May be <code>null</code> if no
217 * default is defined.
218 * @see #getPredicates()
219 * @see #getClosures()
220 * @since Collections15 1.0
221 */
222 public Closure<? super E> getDefaultClosure()
223 {
224 return defaultClosure;
225 }
226
227 }