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