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