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