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 */ 017package org.apache.commons.collections4; 018 019import java.util.Collection; 020import java.util.Map; 021import java.util.Objects; 022 023import org.apache.commons.collections4.functors.ChainedClosure; 024import org.apache.commons.collections4.functors.EqualPredicate; 025import org.apache.commons.collections4.functors.ExceptionClosure; 026import org.apache.commons.collections4.functors.ForClosure; 027import org.apache.commons.collections4.functors.IfClosure; 028import org.apache.commons.collections4.functors.InvokerTransformer; 029import org.apache.commons.collections4.functors.NOPClosure; 030import org.apache.commons.collections4.functors.SwitchClosure; 031import org.apache.commons.collections4.functors.TransformerClosure; 032import org.apache.commons.collections4.functors.WhileClosure; 033 034/** 035 * {@code ClosureUtils} 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>Chained - chains two or more closures together 042 * <li>If - calls one closure or another based on a predicate 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 * <p> 050 * Since v4.1 only closures which are considered to be safe are 051 * Serializable. Closures considered to be unsafe for serialization are: 052 * </p> 053 * <ul> 054 * <li>Invoker 055 * <li>For 056 * <li>While 057 * </ul> 058 * 059 * @since 3.0 060 */ 061public class ClosureUtils { 062 063 /** 064 * Creates a Closure that calls a Transformer each time it is called. 065 * The transformer will be called using the closure's input object. 066 * The transformer's result will be ignored. 067 * 068 * @see org.apache.commons.collections4.functors.TransformerClosure 069 * 070 * @param <E> the type that the closure acts on 071 * @param transformer the transformer to run each time in the closure, null means nop 072 * @return the closure 073 */ 074 public static <E> Closure<E> asClosure(final Transformer<? super E, ?> transformer) { 075 return TransformerClosure.transformerClosure(transformer); 076 } 077 078 /** 079 * Create a new Closure that calls each closure in turn, passing the 080 * result into the next closure. 081 * 082 * @see org.apache.commons.collections4.functors.ChainedClosure 083 * 084 * @param <E> the type that the closure acts on 085 * @param closures an array of closures to chain 086 * @return the {@code chained} closure 087 * @throws NullPointerException if the closures array is null 088 * @throws NullPointerException if any closure in the array is null 089 */ 090 public static <E> Closure<E> chainedClosure(final Closure<? super E>... closures) { 091 return ChainedClosure.chainedClosure(closures); 092 } 093 094 /** 095 * Create a new Closure that calls each closure in turn, passing the 096 * result into the next closure. The ordering is that of the iterator() 097 * method on the collection. 098 * 099 * @see org.apache.commons.collections4.functors.ChainedClosure 100 * 101 * @param <E> the type that the closure acts on 102 * @param closures a collection of closures to chain 103 * @return the {@code chained} closure 104 * @throws NullPointerException if the closures collection is null 105 * @throws NullPointerException if any closure in the collection is null 106 * @throws IllegalArgumentException if the closures collection is empty 107 */ 108 public static <E> Closure<E> chainedClosure(final Collection<? extends Closure<? super E>> closures) { 109 return ChainedClosure.chainedClosure(closures); 110 } 111 112 /** 113 * Creates a Closure that will call the closure once and then repeatedly 114 * until the predicate returns false. 115 * 116 * @see org.apache.commons.collections4.functors.WhileClosure 117 * 118 * @param <E> the type that the closure acts on 119 * @param closure the closure to call repeatedly, not null 120 * @param predicate the predicate to use as an end of loop test, not null 121 * @return the {@code do-while} closure 122 * @throws NullPointerException if either argument is null 123 */ 124 public static <E> Closure<E> doWhileClosure(final Closure<? super E> closure, 125 final Predicate<? super E> predicate) { 126 return WhileClosure.<E>whileClosure(predicate, closure, true); 127 } 128 129 /** 130 * Gets a Closure that always throws an exception. 131 * This could be useful during testing as a placeholder. 132 * 133 * @see org.apache.commons.collections4.functors.ExceptionClosure 134 * 135 * @param <E> the type that the closure acts on 136 * @return the closure 137 */ 138 public static <E> Closure<E> exceptionClosure() { 139 return ExceptionClosure.<E>exceptionClosure(); 140 } 141 142 /** 143 * Creates a Closure that will call the closure {@code count} times. 144 * <p> 145 * A null closure or zero count returns the {@code NOPClosure}. 146 * 147 * @see org.apache.commons.collections4.functors.ForClosure 148 * 149 * @param <E> the type that the closure acts on 150 * @param count the number of times to loop 151 * @param closure the closure to call repeatedly 152 * @return the {@code for} closure 153 */ 154 public static <E> Closure<E> forClosure(final int count, final Closure<? super E> closure) { 155 return ForClosure.forClosure(count, closure); 156 } 157 158 /** 159 * Create a new Closure that calls another closure based on the 160 * result of the specified predicate. 161 * 162 * @see org.apache.commons.collections4.functors.IfClosure 163 * 164 * @param <E> the type that the closure acts on 165 * @param predicate the validating predicate 166 * @param trueClosure the closure called if the predicate is true 167 * @return the {@code if} closure 168 * @throws NullPointerException if the predicate or closure is null 169 * @since 3.2 170 */ 171 public static <E> Closure<E> ifClosure(final Predicate<? super E> predicate, 172 final Closure<? super E> trueClosure) { 173 return IfClosure.<E>ifClosure(predicate, trueClosure); 174 } 175 176 /** 177 * Create a new Closure that calls one of two closures depending 178 * on the specified predicate. 179 * 180 * @see org.apache.commons.collections4.functors.IfClosure 181 * 182 * @param <E> the type that the closure acts on 183 * @param predicate the predicate to switch on 184 * @param trueClosure the closure called if the predicate is true 185 * @param falseClosure the closure called if the predicate is false 186 * @return the {@code switch} closure 187 * @throws NullPointerException if the predicate or either closure is null 188 */ 189 public static <E> Closure<E> ifClosure(final Predicate<? super E> predicate, 190 final Closure<? super E> trueClosure, 191 final Closure<? super E> falseClosure) { 192 return IfClosure.<E>ifClosure(predicate, trueClosure, falseClosure); 193 } 194 195 /** 196 * Creates a Closure that will invoke a specific method on the closure's 197 * input object by reflection. 198 * 199 * @see org.apache.commons.collections4.functors.InvokerTransformer 200 * @see org.apache.commons.collections4.functors.TransformerClosure 201 * 202 * @param <E> the type that the closure acts on 203 * @param methodName the name of the method 204 * @return the {@code invoker} closure 205 * @throws NullPointerException if the method name is null 206 */ 207 public static <E> Closure<E> invokerClosure(final String methodName) { 208 // reuse transformer as it has caching - this is lazy really, should have inner class here 209 return asClosure(InvokerTransformer.<E, Object>invokerTransformer(methodName)); 210 } 211 212 /** 213 * Creates a Closure that will invoke a specific method on the closure's 214 * input object by reflection. 215 * 216 * @see org.apache.commons.collections4.functors.InvokerTransformer 217 * @see org.apache.commons.collections4.functors.TransformerClosure 218 * 219 * @param <E> the type that the closure acts on 220 * @param methodName the name of the method 221 * @param paramTypes the parameter types 222 * @param args the arguments 223 * @return the {@code invoker} closure 224 * @throws NullPointerException if the method name is null 225 * @throws IllegalArgumentException if the paramTypes and args don't match 226 */ 227 public static <E> Closure<E> invokerClosure(final String methodName, final Class<?>[] paramTypes, 228 final Object[] args) { 229 // reuse transformer as it has caching - this is lazy really, should have inner class here 230 return asClosure(InvokerTransformer.<E, Object>invokerTransformer(methodName, paramTypes, args)); 231 } 232 233 /** 234 * Gets a Closure that will do nothing. 235 * This could be useful during testing as a placeholder. 236 * 237 * @see org.apache.commons.collections4.functors.NOPClosure 238 * 239 * @param <E> the type that the closure acts on 240 * @return the closure 241 */ 242 public static <E> Closure<E> nopClosure() { 243 return NOPClosure.<E>nopClosure(); 244 } 245 246 /** 247 * Create a new Closure that calls one of the closures depending 248 * on the predicates. 249 * <p> 250 * The Map consists of Predicate keys and Closure values. A closure 251 * is called if its matching predicate returns true. Each predicate is evaluated 252 * until one returns true. If no predicates evaluate to true, the default 253 * closure is called. The default closure is set in the map with a 254 * null key. The ordering is that of the iterator() method on the entryset 255 * collection of the map. 256 * 257 * @see org.apache.commons.collections4.functors.SwitchClosure 258 * 259 * @param <E> the type that the closure acts on 260 * @param predicatesAndClosures a map of predicates to closures 261 * @return the {@code switch} closure 262 * @throws NullPointerException if the map is null 263 * @throws NullPointerException if any closure in the map is null 264 * @throws IllegalArgumentException if the map is empty 265 * @throws ClassCastException if the map elements are of the wrong type 266 */ 267 public static <E> Closure<E> switchClosure(final Map<Predicate<E>, Closure<E>> predicatesAndClosures) { 268 return SwitchClosure.switchClosure(predicatesAndClosures); 269 } 270 271 /** 272 * Create a new Closure that calls one of the closures depending 273 * on the predicates. 274 * <p> 275 * The closure at array location 0 is called if the predicate at array 276 * location 0 returned true. Each predicate is evaluated 277 * until one returns true. 278 * 279 * @see org.apache.commons.collections4.functors.SwitchClosure 280 * 281 * @param <E> the type that the closure acts on 282 * @param predicates an array of predicates to check, not null 283 * @param closures an array of closures to call, not null 284 * @return the {@code switch} closure 285 * @throws NullPointerException if either array is null 286 * @throws NullPointerException if any element in the arrays is null 287 * @throws IllegalArgumentException if the arrays have different sizes 288 */ 289 public static <E> Closure<E> switchClosure(final Predicate<? super E>[] predicates, 290 final Closure<? super E>[] closures) { 291 return SwitchClosure.<E>switchClosure(predicates, closures, null); 292 } 293 294 /** 295 * Create a new Closure that calls one of the closures depending 296 * on the predicates. 297 * <p> 298 * The closure at array location 0 is called if the predicate at array 299 * location 0 returned true. Each predicate is evaluated 300 * until one returns true. If no predicates evaluate to true, the default 301 * closure is called. 302 * 303 * @see org.apache.commons.collections4.functors.SwitchClosure 304 * 305 * @param <E> the type that the closure acts on 306 * @param predicates an array of predicates to check, not null 307 * @param closures an array of closures to call, not null 308 * @param defaultClosure the default to call if no predicate matches 309 * @return the {@code switch} closure 310 * @throws NullPointerException if either array is null 311 * @throws NullPointerException if any element in the arrays is null 312 * @throws IllegalArgumentException if the arrays are different sizes 313 */ 314 public static <E> Closure<E> switchClosure(final Predicate<? super E>[] predicates, 315 final Closure<? super E>[] closures, 316 final Closure<? super E> defaultClosure) { 317 return SwitchClosure.<E>switchClosure(predicates, closures, defaultClosure); 318 } 319 320 /** 321 * Create a new Closure that uses the input object as a key to find the 322 * closure to call. 323 * <p> 324 * The Map consists of object keys and Closure values. A closure 325 * is called if the input object equals the key. If there is no match, the 326 * default closure is called. The default closure is set in the map 327 * using a null key. 328 * 329 * @see org.apache.commons.collections4.functors.SwitchClosure 330 * 331 * @param <E> the type that the closure acts on 332 * @param objectsAndClosures a map of objects to closures 333 * @return the closure 334 * @throws NullPointerException if the map is null 335 * @throws NullPointerException if any closure in the map is null 336 * @throws IllegalArgumentException if the map is empty 337 */ 338 @SuppressWarnings("unchecked") 339 public static <E> Closure<E> switchMapClosure(final Map<? extends E, Closure<E>> objectsAndClosures) { 340 Objects.requireNonNull(objectsAndClosures, "objectsAndClosures"); 341 final Closure<? super E> def = objectsAndClosures.remove(null); 342 final int size = objectsAndClosures.size(); 343 final Closure<? super E>[] trs = new Closure[size]; 344 final Predicate<E>[] preds = new Predicate[size]; 345 int i = 0; 346 for (final Map.Entry<? extends E, Closure<E>> entry : objectsAndClosures.entrySet()) { 347 preds[i] = EqualPredicate.<E>equalPredicate(entry.getKey()); 348 trs[i] = entry.getValue(); 349 i++; 350 } 351 return ClosureUtils.<E>switchClosure(preds, trs, def); 352 } 353 354 /** 355 * Creates a Closure that will call the closure repeatedly until the 356 * predicate returns false. 357 * 358 * @see org.apache.commons.collections4.functors.WhileClosure 359 * 360 * @param <E> the type that the closure acts on 361 * @param predicate the predicate to use as an end of loop test, not null 362 * @param closure the closure to call repeatedly, not null 363 * @return the {@code while} closure 364 * @throws NullPointerException if either argument is null 365 */ 366 public static <E> Closure<E> whileClosure(final Predicate<? super E> predicate, final Closure<? super E> closure) { 367 return WhileClosure.<E>whileClosure(predicate, closure, false); 368 } 369 370 /** 371 * Don't allow instances. 372 */ 373 private ClosureUtils() { 374 // empty 375 } 376 377}