001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.collections;
018
019 import java.util.Collection;
020 import java.util.Iterator;
021 import java.util.Map;
022
023 import org.apache.commons.collections.functors.ChainedClosure;
024 import org.apache.commons.collections.functors.EqualPredicate;
025 import org.apache.commons.collections.functors.ExceptionClosure;
026 import org.apache.commons.collections.functors.ForClosure;
027 import org.apache.commons.collections.functors.IfClosure;
028 import org.apache.commons.collections.functors.InvokerTransformer;
029 import org.apache.commons.collections.functors.NOPClosure;
030 import org.apache.commons.collections.functors.SwitchClosure;
031 import org.apache.commons.collections.functors.TransformerClosure;
032 import org.apache.commons.collections.functors.WhileClosure;
033
034 /**
035 * <code>ClosureUtils</code> provides reference implementations and utilities
036 * for the Closure functor interface. The supplied closures are:
037 * <ul>
038 * <li>Invoker - invokes a method on the input object
039 * <li>For - repeatedly calls a closure for a fixed number of times
040 * <li>While - repeatedly calls a closure while a predicate is true
041 * <li>DoWhile - repeatedly calls a closure while a predicate is true
042 * <li>Chained - chains two or more closures together
043 * <li>Switch - calls one closure based on one or more predicates
044 * <li>SwitchMap - calls one closure looked up from a Map
045 * <li>Transformer - wraps a Transformer as a Closure
046 * <li>NOP - does nothing
047 * <li>Exception - always throws an exception
048 * </ul>
049 * All the supplied closures are Serializable.
050 *
051 * @since Commons Collections 3.0
052 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
053 *
054 * @author Stephen Colebourne
055 * @author Matt Benson
056 */
057 public class ClosureUtils {
058
059 /**
060 * This class is not normally instantiated.
061 */
062 public ClosureUtils() {
063 super();
064 }
065
066 /**
067 * Gets a Closure that always throws an exception.
068 * This could be useful during testing as a placeholder.
069 *
070 * @see org.apache.commons.collections.functors.ExceptionClosure
071 *
072 * @return the closure
073 */
074 public static Closure exceptionClosure() {
075 return ExceptionClosure.INSTANCE;
076 }
077
078 /**
079 * Gets a Closure that will do nothing.
080 * This could be useful during testing as a placeholder.
081 *
082 * @see org.apache.commons.collections.functors.NOPClosure
083 *
084 * @return the closure
085 */
086 public static Closure nopClosure() {
087 return NOPClosure.INSTANCE;
088 }
089
090 /**
091 * Creates a Closure that calls a Transformer each time it is called.
092 * The transformer will be called using the closure's input object.
093 * The transformer's result will be ignored.
094 *
095 * @see org.apache.commons.collections.functors.TransformerClosure
096 *
097 * @param transformer the transformer to run each time in the closure, null means nop
098 * @return the closure
099 */
100 public static Closure asClosure(Transformer transformer) {
101 return TransformerClosure.getInstance(transformer);
102 }
103
104 /**
105 * Creates a Closure that will call the closure <code>count</code> times.
106 * <p>
107 * A null closure or zero count returns the <code>NOPClosure</code>.
108 *
109 * @see org.apache.commons.collections.functors.ForClosure
110 *
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 Closure forClosure(int count, Closure closure) {
116 return ForClosure.getInstance(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 predicate the predicate to use as an end of loop test, not null
126 * @param closure the closure to call repeatedly, not null
127 * @return the <code>while</code> closure
128 * @throws IllegalArgumentException if either argument is null
129 */
130 public static Closure whileClosure(Predicate predicate, Closure closure) {
131 return WhileClosure.getInstance(predicate, closure, false);
132 }
133
134 /**
135 * Creates a Closure that will call the closure once and then repeatedly
136 * until the predicate returns false.
137 *
138 * @see org.apache.commons.collections.functors.WhileClosure
139 *
140 * @param closure the closure to call repeatedly, not null
141 * @param predicate the predicate to use as an end of loop test, not null
142 * @return the <code>do-while</code> closure
143 * @throws IllegalArgumentException if either argument is null
144 */
145 public static Closure doWhileClosure(Closure closure, Predicate predicate) {
146 return WhileClosure.getInstance(predicate, closure, true);
147 }
148
149 /**
150 * Creates a Closure that will invoke a specific method on the closure's
151 * input object by reflection.
152 *
153 * @see org.apache.commons.collections.functors.InvokerTransformer
154 * @see org.apache.commons.collections.functors.TransformerClosure
155 *
156 * @param methodName the name of the method
157 * @return the <code>invoker</code> closure
158 * @throws IllegalArgumentException if the method name is null
159 */
160 public static Closure invokerClosure(String methodName) {
161 // reuse transformer as it has caching - this is lazy really, should have inner class here
162 return asClosure(InvokerTransformer.getInstance(methodName));
163 }
164
165 /**
166 * Creates a Closure that will invoke a specific method on the closure's
167 * input object by reflection.
168 *
169 * @see org.apache.commons.collections.functors.InvokerTransformer
170 * @see org.apache.commons.collections.functors.TransformerClosure
171 *
172 * @param methodName the name of the method
173 * @param paramTypes the parameter types
174 * @param args the arguments
175 * @return the <code>invoker</code> closure
176 * @throws IllegalArgumentException if the method name is null
177 * @throws IllegalArgumentException if the paramTypes and args don't match
178 */
179 public static Closure invokerClosure(String methodName, Class[] paramTypes, Object[] args) {
180 // reuse transformer as it has caching - this is lazy really, should have inner class here
181 return asClosure(InvokerTransformer.getInstance(methodName, paramTypes, args));
182 }
183
184 /**
185 * Create a new Closure that calls two Closures, passing the result of
186 * the first into the second.
187 *
188 * @see org.apache.commons.collections.functors.ChainedClosure
189 *
190 * @param closure1 the first closure
191 * @param closure2 the second closure
192 * @return the <code>chained</code> closure
193 * @throws IllegalArgumentException if either closure is null
194 */
195 public static Closure chainedClosure(Closure closure1, Closure closure2) {
196 return ChainedClosure.getInstance(closure1, closure2);
197 }
198
199 /**
200 * Create a new Closure that calls each closure in turn, passing the
201 * result into the next closure.
202 *
203 * @see org.apache.commons.collections.functors.ChainedClosure
204 *
205 * @param closures an array of closures to chain
206 * @return the <code>chained</code> closure
207 * @throws IllegalArgumentException if the closures array is null
208 * @throws IllegalArgumentException if any closure in the array is null
209 */
210 public static Closure chainedClosure(Closure[] closures) {
211 return ChainedClosure.getInstance(closures);
212 }
213
214 /**
215 * Create a new Closure that calls each closure in turn, passing the
216 * result into the next closure. The ordering is that of the iterator()
217 * method on the collection.
218 *
219 * @see org.apache.commons.collections.functors.ChainedClosure
220 *
221 * @param closures a collection of closures to chain
222 * @return the <code>chained</code> closure
223 * @throws IllegalArgumentException if the closures collection is null
224 * @throws IllegalArgumentException if the closures collection is empty
225 * @throws IllegalArgumentException if any closure in the collection is null
226 */
227 public static Closure chainedClosure(Collection closures) {
228 return ChainedClosure.getInstance(closures);
229 }
230
231 /**
232 * Create a new Closure that calls another closure based on the
233 * result of the specified predicate.
234 *
235 * @see org.apache.commons.collections.functors.IfClosure
236 *
237 * @param predicate the validating predicate
238 * @param trueClosure the closure called if the predicate is true
239 * @return the <code>if</code> closure
240 * @throws IllegalArgumentException if the predicate is null
241 * @throws IllegalArgumentException if the closure is null
242 * @since Commons Collections 3.2
243 */
244 public static Closure ifClosure(Predicate predicate, Closure trueClosure) {
245 return IfClosure.getInstance(predicate, trueClosure);
246 }
247
248 /**
249 * Create a new Closure that calls one of two closures depending
250 * on the specified predicate.
251 *
252 * @see org.apache.commons.collections.functors.IfClosure
253 *
254 * @param predicate the predicate to switch on
255 * @param trueClosure the closure called if the predicate is true
256 * @param falseClosure the closure called if the predicate is false
257 * @return the <code>switch</code> closure
258 * @throws IllegalArgumentException if the predicate is null
259 * @throws IllegalArgumentException if either closure is null
260 */
261 public static Closure ifClosure(Predicate predicate, Closure trueClosure, Closure falseClosure) {
262 return IfClosure.getInstance(predicate, trueClosure, falseClosure);
263 }
264
265 /**
266 * Create a new Closure that calls one of the closures depending
267 * on the predicates.
268 * <p>
269 * The closure at array location 0 is called if the predicate at array
270 * location 0 returned true. Each predicate is evaluated
271 * until one returns true.
272 *
273 * @see org.apache.commons.collections.functors.SwitchClosure
274 *
275 * @param predicates an array of predicates to check, not null
276 * @param closures an array of closures to call, not null
277 * @return the <code>switch</code> closure
278 * @throws IllegalArgumentException if the either array is null
279 * @throws IllegalArgumentException if any element in the arrays is null
280 * @throws IllegalArgumentException if the arrays are different sizes
281 */
282 public static Closure switchClosure(Predicate[] predicates, Closure[] closures) {
283 return SwitchClosure.getInstance(predicates, closures, null);
284 }
285
286 /**
287 * Create a new Closure that calls one of the closures depending
288 * on the predicates.
289 * <p>
290 * The closure at array location 0 is called if the predicate at array
291 * location 0 returned true. Each predicate is evaluated
292 * until one returns true. If no predicates evaluate to true, the default
293 * closure is called.
294 *
295 * @see org.apache.commons.collections.functors.SwitchClosure
296 *
297 * @param predicates an array of predicates to check, not null
298 * @param closures an array of closures to call, not null
299 * @param defaultClosure the default to call if no predicate matches
300 * @return the <code>switch</code> closure
301 * @throws IllegalArgumentException if the either array is null
302 * @throws IllegalArgumentException if any element in the arrays is null
303 * @throws IllegalArgumentException if the arrays are different sizes
304 */
305 public static Closure switchClosure(Predicate[] predicates, Closure[] closures, Closure defaultClosure) {
306 return SwitchClosure.getInstance(predicates, closures, defaultClosure);
307 }
308
309 /**
310 * Create a new Closure that calls one of the closures depending
311 * on the predicates.
312 * <p>
313 * The Map consists of Predicate keys and Closure values. A closure
314 * is called if its matching predicate returns true. Each predicate is evaluated
315 * until one returns true. If no predicates evaluate to true, the default
316 * closure is called. The default closure is set in the map with a
317 * null key. The ordering is that of the iterator() method on the entryset
318 * collection of the map.
319 *
320 * @see org.apache.commons.collections.functors.SwitchClosure
321 *
322 * @param predicatesAndClosures a map of predicates to closures
323 * @return the <code>switch</code> closure
324 * @throws IllegalArgumentException if the map is null
325 * @throws IllegalArgumentException if the map is empty
326 * @throws IllegalArgumentException if any closure in the map is null
327 * @throws ClassCastException if the map elements are of the wrong type
328 */
329 public static Closure switchClosure(Map predicatesAndClosures) {
330 return SwitchClosure.getInstance(predicatesAndClosures);
331 }
332
333 /**
334 * Create a new Closure that uses the input object as a key to find the
335 * closure to call.
336 * <p>
337 * The Map consists of object keys and Closure values. A closure
338 * is called if the input object equals the key. If there is no match, the
339 * default closure is called. The default closure is set in the map
340 * using a null key.
341 *
342 * @see org.apache.commons.collections.functors.SwitchClosure
343 *
344 * @param objectsAndClosures a map of objects to closures
345 * @return the closure
346 * @throws IllegalArgumentException if the map is null
347 * @throws IllegalArgumentException if the map is empty
348 * @throws IllegalArgumentException if any closure in the map is null
349 */
350 public static Closure switchMapClosure(Map objectsAndClosures) {
351 Closure[] trs = null;
352 Predicate[] preds = null;
353 if (objectsAndClosures == null) {
354 throw new IllegalArgumentException("The object and closure map must not be null");
355 }
356 Closure def = (Closure) objectsAndClosures.remove(null);
357 int size = objectsAndClosures.size();
358 trs = new Closure[size];
359 preds = new Predicate[size];
360 int i = 0;
361 for (Iterator it = objectsAndClosures.entrySet().iterator(); it.hasNext();) {
362 Map.Entry entry = (Map.Entry) it.next();
363 preds[i] = EqualPredicate.getInstance(entry.getKey());
364 trs[i] = (Closure) entry.getValue();
365 i++;
366 }
367 return switchClosure(preds, trs, def);
368 }
369
370 }