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.collections;
18
19 import java.util.Collection;
20 import java.util.Map;
21
22 import org.apache.commons.collections.functors.ChainedClosure;
23 import org.apache.commons.collections.functors.EqualPredicate;
24 import org.apache.commons.collections.functors.ExceptionClosure;
25 import org.apache.commons.collections.functors.ForClosure;
26 import org.apache.commons.collections.functors.IfClosure;
27 import org.apache.commons.collections.functors.InvokerTransformer;
28 import org.apache.commons.collections.functors.NOPClosure;
29 import org.apache.commons.collections.functors.SwitchClosure;
30 import org.apache.commons.collections.functors.TransformerClosure;
31 import org.apache.commons.collections.functors.WhileClosure;
32
33 /**
34 * <code>ClosureUtils</code> provides reference implementations and utilities
35 * for the Closure functor interface. The supplied closures are:
36 * <ul>
37 * <li>Invoker - invokes a method on the input object
38 * <li>For - repeatedly calls a closure for a fixed number of times
39 * <li>While - repeatedly calls a closure while a predicate is true
40 * <li>DoWhile - repeatedly calls a closure while a predicate is true
41 * <li>Chained - chains two or more closures together
42 * <li>Switch - calls one closure based on one or more predicates
43 * <li>SwitchMap - calls one closure looked up from a Map
44 * <li>Transformer - wraps a Transformer as a Closure
45 * <li>NOP - does nothing
46 * <li>Exception - always throws an exception
47 * </ul>
48 * All the supplied closures are Serializable.
49 *
50 * @since 3.0
51 * @version $Id: ClosureUtils.java 1443602 2013-02-07 17:00:23Z tn $
52 */
53 public class ClosureUtils {
54
55 /**
56 * This class is not normally instantiated.
57 */
58 public ClosureUtils() {
59 super();
60 }
61
62 /**
63 * Gets a Closure that always throws an exception.
64 * This could be useful during testing as a placeholder.
65 *
66 * @see org.apache.commons.collections.functors.ExceptionClosure
67 *
68 * @param <E> the type that the closure acts on
69 * @return the closure
70 */
71 public static <E> Closure<E> exceptionClosure() {
72 return ExceptionClosure.<E>exceptionClosure();
73 }
74
75 /**
76 * Gets a Closure that will do nothing.
77 * This could be useful during testing as a placeholder.
78 *
79 * @see org.apache.commons.collections.functors.NOPClosure
80 *
81 * @param <E> the type that the closure acts on
82 * @return the closure
83 */
84 public static <E> Closure<E> nopClosure() {
85 return NOPClosure.<E>nopClosure();
86 }
87
88 /**
89 * Creates a Closure that calls a Transformer each time it is called.
90 * The transformer will be called using the closure's input object.
91 * The transformer's result will be ignored.
92 *
93 * @see org.apache.commons.collections.functors.TransformerClosure
94 *
95 * @param <E> the type that the closure acts on
96 * @param transformer the transformer to run each time in the closure, null means nop
97 * @return the closure
98 */
99 public static <E> Closure<E> asClosure(final Transformer<? super E, ?> transformer) {
100 return TransformerClosure.transformerClosure(transformer);
101 }
102
103 /**
104 * Creates a Closure that will call the closure <code>count</code> times.
105 * <p>
106 * A null closure or zero count returns the <code>NOPClosure</code>.
107 *
108 * @see org.apache.commons.collections.functors.ForClosure
109 *
110 * @param <E> the type that the closure acts on
111 * @param count the number of times to loop
112 * @param closure the closure to call repeatedly
113 * @return the <code>for</code> closure
114 */
115 public static <E> Closure<E> forClosure(final int count, final Closure<? super E> closure) {
116 return ForClosure.forClosure(count, closure);
117 }
118
119 /**
120 * Creates a Closure that will call the closure repeatedly until the
121 * predicate returns false.
122 *
123 * @see org.apache.commons.collections.functors.WhileClosure
124 *
125 * @param <E> the type that the closure acts on
126 * @param predicate the predicate to use as an end of loop test, not null
127 * @param closure the closure to call repeatedly, not null
128 * @return the <code>while</code> closure
129 * @throws IllegalArgumentException if either argument is null
130 */
131 public static <E> Closure<E> whileClosure(final Predicate<? super E> predicate, final Closure<? super E> closure) {
132 return WhileClosure.<E>whileClosure(predicate, closure, false);
133 }
134
135 /**
136 * Creates a Closure that will call the closure once and then repeatedly
137 * until the predicate returns false.
138 *
139 * @see org.apache.commons.collections.functors.WhileClosure
140 *
141 * @param <E> the type that the closure acts on
142 * @param closure the closure to call repeatedly, not null
143 * @param predicate the predicate to use as an end of loop test, not null
144 * @return the <code>do-while</code> closure
145 * @throws IllegalArgumentException if either argument is null
146 */
147 public static <E> Closure<E> doWhileClosure(final Closure<? super E> closure,
148 final Predicate<? super E> predicate) {
149 return WhileClosure.<E>whileClosure(predicate, closure, true);
150 }
151
152 /**
153 * Creates a Closure that will invoke a specific method on the closure's
154 * input object by reflection.
155 *
156 * @see org.apache.commons.collections.functors.InvokerTransformer
157 * @see org.apache.commons.collections.functors.TransformerClosure
158 *
159 * @param <E> the type that the closure acts on
160 * @param methodName the name of the method
161 * @return the <code>invoker</code> closure
162 * @throws IllegalArgumentException if the method name is null
163 */
164 public static <E> Closure<E> invokerClosure(final String methodName) {
165 // reuse transformer as it has caching - this is lazy really, should have inner class here
166 return asClosure(InvokerTransformer.<E, Object>invokerTransformer(methodName));
167 }
168
169 /**
170 * Creates a Closure that will invoke a specific method on the closure's
171 * input object by reflection.
172 *
173 * @see org.apache.commons.collections.functors.InvokerTransformer
174 * @see org.apache.commons.collections.functors.TransformerClosure
175 *
176 * @param <E> the type that the closure acts on
177 * @param methodName the name of the method
178 * @param paramTypes the parameter types
179 * @param args the arguments
180 * @return the <code>invoker</code> closure
181 * @throws IllegalArgumentException if the method name is null
182 * @throws IllegalArgumentException if the paramTypes and args don't match
183 */
184 public static <E> Closure<E> invokerClosure(final String methodName, final Class<?>[] paramTypes,
185 final Object[] args) {
186 // reuse transformer as it has caching - this is lazy really, should have inner class here
187 return asClosure(InvokerTransformer.<E, Object>invokerTransformer(methodName, paramTypes, args));
188 }
189
190 /**
191 * Create a new Closure that calls two Closures, passing the result of
192 * the first into the second.
193 *
194 * @see org.apache.commons.collections.functors.ChainedClosure
195 *
196 * @param <E> the type that the closure acts on
197 * @param closure1 the first closure
198 * @param closure2 the second closure
199 * @return the <code>chained</code> closure
200 * @throws IllegalArgumentException if either closure is null
201 */
202 public static <E> Closure<E> chainedClosure(final Closure<? super E> closure1, final Closure<? super E> closure2) {
203 return ChainedClosure.<E>chainedClosure(closure1, closure2);
204 }
205
206 /**
207 * Create a new Closure that calls each closure in turn, passing the
208 * result into the next closure.
209 *
210 * @see org.apache.commons.collections.functors.ChainedClosure
211 *
212 * @param <E> the type that the closure acts on
213 * @param closures an array of closures to chain
214 * @return the <code>chained</code> closure
215 * @throws IllegalArgumentException if the closures array is null
216 * @throws IllegalArgumentException if any closure in the array is null
217 */
218 public static <E> Closure<E> chainedClosure(final Closure<? super E>... closures) {
219 return ChainedClosure.chainedClosure(closures);
220 }
221
222 /**
223 * Create a new Closure that calls each closure in turn, passing the
224 * result into the next closure. The ordering is that of the iterator()
225 * method on the collection.
226 *
227 * @see org.apache.commons.collections.functors.ChainedClosure
228 *
229 * @param <E> the type that the closure acts on
230 * @param closures a collection of closures to chain
231 * @return the <code>chained</code> closure
232 * @throws IllegalArgumentException if the closures collection is null
233 * @throws IllegalArgumentException if the closures collection is empty
234 * @throws IllegalArgumentException if any closure in the collection is null
235 */
236 public static <E> Closure<E> chainedClosure(final Collection<Closure<E>> closures) {
237 return ChainedClosure.chainedClosure(closures);
238 }
239
240 /**
241 * Create a new Closure that calls another closure based on the
242 * result of the specified predicate.
243 *
244 * @see org.apache.commons.collections.functors.IfClosure
245 *
246 * @param <E> the type that the closure acts on
247 * @param predicate the validating predicate
248 * @param trueClosure the closure called if the predicate is true
249 * @return the <code>if</code> closure
250 * @throws IllegalArgumentException if the predicate is null
251 * @throws IllegalArgumentException if the closure is null
252 * @since 3.2
253 */
254 public static <E> Closure<E> ifClosure(final Predicate<? super E> predicate,
255 final Closure<? super E> trueClosure) {
256 return IfClosure.<E>ifClosure(predicate, trueClosure);
257 }
258
259 /**
260 * Create a new Closure that calls one of two closures depending
261 * on the specified predicate.
262 *
263 * @see org.apache.commons.collections.functors.IfClosure
264 *
265 * @param <E> the type that the closure acts on
266 * @param predicate the predicate to switch on
267 * @param trueClosure the closure called if the predicate is true
268 * @param falseClosure the closure called if the predicate is false
269 * @return the <code>switch</code> closure
270 * @throws IllegalArgumentException if the predicate is null
271 * @throws IllegalArgumentException if either closure is null
272 */
273 public static <E> Closure<E> ifClosure(final Predicate<? super E> predicate,
274 final Closure<? super E> trueClosure,
275 final Closure<? super E> falseClosure) {
276 return IfClosure.<E>ifClosure(predicate, trueClosure, falseClosure);
277 }
278
279 /**
280 * Create a new Closure that calls one of the closures depending
281 * on the predicates.
282 * <p>
283 * The closure at array location 0 is called if the predicate at array
284 * location 0 returned true. Each predicate is evaluated
285 * until one returns true.
286 *
287 * @see org.apache.commons.collections.functors.SwitchClosure
288 *
289 * @param <E> the type that the closure acts on
290 * @param predicates an array of predicates to check, not null
291 * @param closures an array of closures to call, not null
292 * @return the <code>switch</code> closure
293 * @throws IllegalArgumentException if the either array is null
294 * @throws IllegalArgumentException if any element in the arrays is null
295 * @throws IllegalArgumentException if the arrays are different sizes
296 */
297 public static <E> Closure<E> switchClosure(final Predicate<? super E>[] predicates,
298 final Closure<? super E>[] closures) {
299 return SwitchClosure.<E>switchClosure(predicates, closures, null);
300 }
301
302 /**
303 * Create a new Closure that calls one of the closures depending
304 * on the predicates.
305 * <p>
306 * The closure at array location 0 is called if the predicate at array
307 * location 0 returned true. Each predicate is evaluated
308 * until one returns true. If no predicates evaluate to true, the default
309 * closure is called.
310 *
311 * @see org.apache.commons.collections.functors.SwitchClosure
312 *
313 * @param <E> the type that the closure acts on
314 * @param predicates an array of predicates to check, not null
315 * @param closures an array of closures to call, not null
316 * @param defaultClosure the default to call if no predicate matches
317 * @return the <code>switch</code> closure
318 * @throws IllegalArgumentException if the either array is null
319 * @throws IllegalArgumentException if any element in the arrays is null
320 * @throws IllegalArgumentException if the arrays are different sizes
321 */
322 public static <E> Closure<E> switchClosure(final Predicate<? super E>[] predicates,
323 final Closure<? super E>[] closures,
324 final Closure<? super E> defaultClosure) {
325 return SwitchClosure.<E>switchClosure(predicates, closures, defaultClosure);
326 }
327
328 /**
329 * Create a new Closure that calls one of the closures depending
330 * on the predicates.
331 * <p>
332 * The Map consists of Predicate keys and Closure values. A closure
333 * is called if its matching predicate returns true. Each predicate is evaluated
334 * until one returns true. If no predicates evaluate to true, the default
335 * closure is called. The default closure is set in the map with a
336 * null key. The ordering is that of the iterator() method on the entryset
337 * collection of the map.
338 *
339 * @see org.apache.commons.collections.functors.SwitchClosure
340 *
341 * @param <E> the type that the closure acts on
342 * @param predicatesAndClosures a map of predicates to closures
343 * @return the <code>switch</code> closure
344 * @throws IllegalArgumentException if the map is null
345 * @throws IllegalArgumentException if the map is empty
346 * @throws IllegalArgumentException if any closure in the map is null
347 * @throws ClassCastException if the map elements are of the wrong type
348 */
349 public static <E> Closure<E> switchClosure(final Map<Predicate<E>, Closure<E>> predicatesAndClosures) {
350 return SwitchClosure.switchClosure(predicatesAndClosures);
351 }
352
353 /**
354 * Create a new Closure that uses the input object as a key to find the
355 * closure to call.
356 * <p>
357 * The Map consists of object keys and Closure values. A closure
358 * is called if the input object equals the key. If there is no match, the
359 * default closure is called. The default closure is set in the map
360 * using a null key.
361 *
362 * @see org.apache.commons.collections.functors.SwitchClosure
363 *
364 * @param <E> the type that the closure acts on
365 * @param objectsAndClosures a map of objects to closures
366 * @return the closure
367 * @throws IllegalArgumentException if the map is null
368 * @throws IllegalArgumentException if the map is empty
369 * @throws IllegalArgumentException if any closure in the map is null
370 */
371 @SuppressWarnings("unchecked")
372 public static <E> Closure<E> switchMapClosure(final Map<? extends E, Closure<E>> objectsAndClosures) {
373 if (objectsAndClosures == null) {
374 throw new IllegalArgumentException("The object and closure map must not be null");
375 }
376 final Closure<? super E> def = objectsAndClosures.remove(null);
377 final int size = objectsAndClosures.size();
378 final Closure<? super E>[] trs = new Closure[size];
379 final Predicate<E>[] preds = new Predicate[size];
380 int i = 0;
381 for (final Map.Entry<? extends E, Closure<E>> entry : objectsAndClosures.entrySet()) {
382 preds[i] = EqualPredicate.<E>equalPredicate(entry.getKey());
383 trs[i] = entry.getValue();
384 i++;
385 }
386 return ClosureUtils.<E>switchClosure(preds, trs, def);
387 }
388
389 }