1. /*
  2. * Copyright 2002-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.collections;
  17. import java.util.Collection;
  18. import java.util.Iterator;
  19. import java.util.Map;
  20. import org.apache.commons.collections.functors.ChainedClosure;
  21. import org.apache.commons.collections.functors.EqualPredicate;
  22. import org.apache.commons.collections.functors.ExceptionClosure;
  23. import org.apache.commons.collections.functors.ForClosure;
  24. import org.apache.commons.collections.functors.IfClosure;
  25. import org.apache.commons.collections.functors.InvokerTransformer;
  26. import org.apache.commons.collections.functors.NOPClosure;
  27. import org.apache.commons.collections.functors.SwitchClosure;
  28. import org.apache.commons.collections.functors.TransformerClosure;
  29. import org.apache.commons.collections.functors.WhileClosure;
  30. /**
  31. * <code>ClosureUtils</code> provides reference implementations and utilities
  32. * for the Closure functor interface. The supplied closures are:
  33. * <ul>
  34. * <li>Invoker - invokes a method on the input object
  35. * <li>For - repeatedly calls a closure for a fixed number of times
  36. * <li>While - repeatedly calls a closure while a predicate is true
  37. * <li>DoWhile - repeatedly calls a closure while a predicate is true
  38. * <li>Chained - chains two or more closures together
  39. * <li>Switch - calls one closure based on one or more predicates
  40. * <li>SwitchMap - calls one closure looked up from a Map
  41. * <li>Transformer - wraps a Transformer as a Closure
  42. * <li>NOP - does nothing
  43. * <li>Exception - always throws an exception
  44. * </ul>
  45. * All the supplied closures are Serializable.
  46. *
  47. * @since Commons Collections 3.0
  48. * @version $Revision: 1.9 $ $Date: 2004/05/26 21:50:52 $
  49. *
  50. * @author Stephen Colebourne
  51. */
  52. public class ClosureUtils {
  53. /**
  54. * This class is not normally instantiated.
  55. */
  56. public ClosureUtils() {
  57. super();
  58. }
  59. /**
  60. * Gets a Closure that always throws an exception.
  61. * This could be useful during testing as a placeholder.
  62. *
  63. * @see org.apache.commons.collections.functors.ExceptionClosure
  64. *
  65. * @return the closure
  66. */
  67. public static Closure exceptionClosure() {
  68. return ExceptionClosure.INSTANCE;
  69. }
  70. /**
  71. * Gets a Closure that will do nothing.
  72. * This could be useful during testing as a placeholder.
  73. *
  74. * @see org.apache.commons.collections.functors.NOPClosure
  75. *
  76. * @return the closure
  77. */
  78. public static Closure nopClosure() {
  79. return NOPClosure.INSTANCE;
  80. }
  81. /**
  82. * Creates a Closure that calls a Transformer each time it is called.
  83. * The transformer will be called using the closure's input object.
  84. * The transformer's result will be ignored.
  85. *
  86. * @see org.apache.commons.collections.functors.TransformerClosure
  87. *
  88. * @param transformer the transformer to run each time in the closure, null means nop
  89. * @return the closure
  90. */
  91. public static Closure asClosure(Transformer transformer) {
  92. return TransformerClosure.getInstance(transformer);
  93. }
  94. /**
  95. * Creates a Closure that will call the closure <code>count</code> times.
  96. * <p>
  97. * A null closure or zero count returns the <code>NOPClosure</code>.
  98. *
  99. * @see org.apache.commons.collections.functors.ForClosure
  100. *
  101. * @param count the number of times to loop
  102. * @param closure the closure to call repeatedly
  103. * @return the <code>for</code> closure
  104. */
  105. public static Closure forClosure(int count, Closure closure) {
  106. return ForClosure.getInstance(count, closure);
  107. }
  108. /**
  109. * Creates a Closure that will call the closure repeatedly until the
  110. * predicate returns false.
  111. *
  112. * @see org.apache.commons.collections.functors.WhileClosure
  113. *
  114. * @param predicate the predicate to use as an end of loop test, not null
  115. * @param closure the closure to call repeatedly, not null
  116. * @return the <code>while</code> closure
  117. * @throws IllegalArgumentException if either argument is null
  118. */
  119. public static Closure whileClosure(Predicate predicate, Closure closure) {
  120. return WhileClosure.getInstance(predicate, closure, false);
  121. }
  122. /**
  123. * Creates a Closure that will call the closure once and then repeatedly
  124. * until the predicate returns false.
  125. *
  126. * @see org.apache.commons.collections.functors.WhileClosure
  127. *
  128. * @param closure the closure to call repeatedly, not null
  129. * @param predicate the predicate to use as an end of loop test, not null
  130. * @return the <code>do-while</code> closure
  131. * @throws IllegalArgumentException if either argument is null
  132. */
  133. public static Closure doWhileClosure(Closure closure, Predicate predicate) {
  134. return WhileClosure.getInstance(predicate, closure, true);
  135. }
  136. /**
  137. * Creates a Closure that will invoke a specific method on the closure's
  138. * input object by reflection.
  139. *
  140. * @see org.apache.commons.collections.functors.InvokerTransformer
  141. * @see org.apache.commons.collections.functors.TransformerClosure
  142. *
  143. * @param methodName the name of the method
  144. * @return the <code>invoker</code> closure
  145. * @throws IllegalArgumentException if the method name is null
  146. */
  147. public static Closure invokerClosure(String methodName) {
  148. // reuse transformer as it has caching - this is lazy really, should have inner class here
  149. return asClosure(InvokerTransformer.getInstance(methodName));
  150. }
  151. /**
  152. * Creates a Closure that will invoke a specific method on the closure's
  153. * input object by reflection.
  154. *
  155. * @see org.apache.commons.collections.functors.InvokerTransformer
  156. * @see org.apache.commons.collections.functors.TransformerClosure
  157. *
  158. * @param methodName the name of the method
  159. * @param paramTypes the parameter types
  160. * @param args the arguments
  161. * @return the <code>invoker</code> closure
  162. * @throws IllegalArgumentException if the method name is null
  163. * @throws IllegalArgumentException if the paramTypes and args don't match
  164. */
  165. public static Closure invokerClosure(String methodName, Class[] paramTypes, Object[] args) {
  166. // reuse transformer as it has caching - this is lazy really, should have inner class here
  167. return asClosure(InvokerTransformer.getInstance(methodName, paramTypes, args));
  168. }
  169. /**
  170. * Create a new Closure that calls two Closures, passing the result of
  171. * the first into the second.
  172. *
  173. * @see org.apache.commons.collections.functors.ChainedClosure
  174. *
  175. * @param closure1 the first closure
  176. * @param closure2 the second closure
  177. * @return the <code>chained</code> closure
  178. * @throws IllegalArgumentException if either closure is null
  179. */
  180. public static Closure chainedClosure(Closure closure1, Closure closure2) {
  181. return ChainedClosure.getInstance(closure1, closure2);
  182. }
  183. /**
  184. * Create a new Closure that calls each closure in turn, passing the
  185. * result into the next closure.
  186. *
  187. * @see org.apache.commons.collections.functors.ChainedClosure
  188. *
  189. * @param closures an array of closures to chain
  190. * @return the <code>chained</code> closure
  191. * @throws IllegalArgumentException if the closures array is null
  192. * @throws IllegalArgumentException if any closure in the array is null
  193. */
  194. public static Closure chainedClosure(Closure[] closures) {
  195. return ChainedClosure.getInstance(closures);
  196. }
  197. /**
  198. * Create a new Closure that calls each closure in turn, passing the
  199. * result into the next closure. The ordering is that of the iterator()
  200. * method on the collection.
  201. *
  202. * @see org.apache.commons.collections.functors.ChainedClosure
  203. *
  204. * @param closures a collection of closures to chain
  205. * @return the <code>chained</code> closure
  206. * @throws IllegalArgumentException if the closures collection is null
  207. * @throws IllegalArgumentException if the closures collection is empty
  208. * @throws IllegalArgumentException if any closure in the collection is null
  209. */
  210. public static Closure chainedClosure(Collection closures) {
  211. return ChainedClosure.getInstance(closures);
  212. }
  213. /**
  214. * Create a new Closure that calls one of two closures depending
  215. * on the specified predicate.
  216. *
  217. * @see org.apache.commons.collections.functors.IfClosure
  218. *
  219. * @param predicate the predicate to switch on
  220. * @param trueClosure the closure called if the predicate is true
  221. * @param falseClosure the closure called if the predicate is false
  222. * @return the <code>switch</code> closure
  223. * @throws IllegalArgumentException if the predicate is null
  224. * @throws IllegalArgumentException if either closure is null
  225. */
  226. public static Closure ifClosure(Predicate predicate, Closure trueClosure, Closure falseClosure) {
  227. return IfClosure.getInstance(predicate, trueClosure, falseClosure);
  228. }
  229. /**
  230. * Create a new Closure that calls one of the closures depending
  231. * on the predicates.
  232. * <p>
  233. * The closure at array location 0 is called if the predicate at array
  234. * location 0 returned true. Each predicate is evaluated
  235. * until one returns true.
  236. *
  237. * @see org.apache.commons.collections.functors.SwitchClosure
  238. *
  239. * @param predicates an array of predicates to check, not null
  240. * @param closures an array of closures to call, not null
  241. * @return the <code>switch</code> closure
  242. * @throws IllegalArgumentException if the either array is null
  243. * @throws IllegalArgumentException if any element in the arrays is null
  244. * @throws IllegalArgumentException if the arrays are different sizes
  245. */
  246. public static Closure switchClosure(Predicate[] predicates, Closure[] closures) {
  247. return SwitchClosure.getInstance(predicates, closures, null);
  248. }
  249. /**
  250. * Create a new Closure that calls one of the closures depending
  251. * on the predicates.
  252. * <p>
  253. * The closure at array location 0 is called if the predicate at array
  254. * location 0 returned true. Each predicate is evaluated
  255. * until one returns true. If no predicates evaluate to true, the default
  256. * closure is called.
  257. *
  258. * @see org.apache.commons.collections.functors.SwitchClosure
  259. *
  260. * @param predicates an array of predicates to check, not null
  261. * @param closures an array of closures to call, not null
  262. * @param defaultClosure the default to call if no predicate matches
  263. * @return the <code>switch</code> closure
  264. * @throws IllegalArgumentException if the either array is null
  265. * @throws IllegalArgumentException if any element in the arrays is null
  266. * @throws IllegalArgumentException if the arrays are different sizes
  267. */
  268. public static Closure switchClosure(Predicate[] predicates, Closure[] closures, Closure defaultClosure) {
  269. return SwitchClosure.getInstance(predicates, closures, defaultClosure);
  270. }
  271. /**
  272. * Create a new Closure that calls one of the closures depending
  273. * on the predicates.
  274. * <p>
  275. * The Map consists of Predicate keys and Closure values. A closure
  276. * is called if its matching predicate returns true. Each predicate is evaluated
  277. * until one returns true. If no predicates evaluate to true, the default
  278. * closure is called. The default closure is set in the map with a
  279. * null key. The ordering is that of the iterator() method on the entryset
  280. * collection of the map.
  281. *
  282. * @see org.apache.commons.collections.functors.SwitchClosure
  283. *
  284. * @param predicatesAndClosures a map of predicates to closures
  285. * @return the <code>switch</code> closure
  286. * @throws IllegalArgumentException if the map is null
  287. * @throws IllegalArgumentException if the map is empty
  288. * @throws IllegalArgumentException if any closure in the map is null
  289. * @throws ClassCastException if the map elements are of the wrong type
  290. */
  291. public static Closure switchClosure(Map predicatesAndClosures) {
  292. return SwitchClosure.getInstance(predicatesAndClosures);
  293. }
  294. /**
  295. * Create a new Closure that uses the input object as a key to find the
  296. * closure to call.
  297. * <p>
  298. * The Map consists of object keys and Closure values. A closure
  299. * is called if the input object equals the key. If there is no match, the
  300. * default closure is called. The default closure is set in the map
  301. * using a null key.
  302. *
  303. * @see org.apache.commons.collections.functors.SwitchClosure
  304. *
  305. * @param objectsAndClosures a map of objects to closures
  306. * @return the closure
  307. * @throws IllegalArgumentException if the map is null
  308. * @throws IllegalArgumentException if the map is empty
  309. * @throws IllegalArgumentException if any closure in the map is null
  310. */
  311. public static Closure switchMapClosure(Map objectsAndClosures) {
  312. Closure[] trs = null;
  313. Predicate[] preds = null;
  314. if (objectsAndClosures == null) {
  315. throw new IllegalArgumentException("The object and closure map must not be null");
  316. }
  317. Closure def = (Closure) objectsAndClosures.remove(null);
  318. int size = objectsAndClosures.size();
  319. trs = new Closure[size];
  320. preds = new Predicate[size];
  321. int i = 0;
  322. for (Iterator it = objectsAndClosures.entrySet().iterator(); it.hasNext();) {
  323. Map.Entry entry = (Map.Entry) it.next();
  324. preds[i] = EqualPredicate.getInstance(entry.getKey());
  325. trs[i] = (Closure) entry.getValue();
  326. i++;
  327. }
  328. return switchClosure(preds, trs, def);
  329. }
  330. }