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