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