001 /* 002 * Copyright 2002-2004 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy 006 * of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations 014 * under the License. 015 */ 016 package org.springframework.rules.closure.support; 017 018 import org.springframework.rules.closure.Closure; 019 import org.springframework.rules.constraint.Constraint; 020 import org.springframework.rules.closure.ElementGenerator; 021 022 /** 023 * Base superclass for process templates. 024 * 025 * @author Keith Donald 026 */ 027 public abstract class AbstractElementGenerator implements ElementGenerator { 028 029 /** Wrapping instance for internal usage. */ 030 private ElementGenerator wrappedGenerator; 031 032 /** <code>true</code> if this ElementGenerator may run once. */ 033 private boolean runOnce = false; 034 035 /** Current status of this ElementGenerator. */ 036 private volatile ProcessStatus status = ProcessStatus.CREATED; 037 038 /** 039 * Default constructor. 040 */ 041 protected AbstractElementGenerator() { 042 } 043 044 /** 045 * Constructor. 046 * 047 * @param runOnce <code>true</code> if this ElementGenerator may only be 048 * run once (will throw an Exception if called more than once). 049 */ 050 protected AbstractElementGenerator(boolean runOnce) { 051 this.runOnce = runOnce; 052 } 053 054 /** 055 * Create an wrapper instance for internal usage. 056 * 057 * @param wrappedTemplate 058 */ 059 private AbstractElementGenerator(ElementGenerator wrappedTemplate) { 060 this.wrappedGenerator = wrappedTemplate; 061 } 062 063 /** 064 * @return the wrapped ElementGenerator or <code>null</code>. 065 */ 066 protected ElementGenerator getWrappedTemplate() { 067 return wrappedGenerator; 068 } 069 070 /** 071 * {@inheritDoc} 072 */ 073 public boolean allTrue(Constraint constraint) { 074 WhileTrueController controller = new WhileTrueController(this, constraint); 075 run(controller); 076 return controller.allTrue(); 077 } 078 079 /** 080 * {@inheritDoc} 081 */ 082 public boolean anyTrue(Constraint constraint) { 083 return findFirst(constraint, null) != null; 084 } 085 086 /** 087 * {@inheritDoc} 088 */ 089 public ElementGenerator findAll(final Constraint constraint) { 090 return new AbstractElementGenerator(this) { 091 public void run(final Closure closure) { 092 getWrappedTemplate().run(new IfBlock(constraint, closure)); 093 } 094 }; 095 } 096 097 /** 098 * {@inheritDoc} 099 */ 100 public Object findFirst(Constraint constraint) { 101 return findFirst(constraint, null); 102 } 103 104 /** 105 * {@inheritDoc} 106 */ 107 public Object findFirst(Constraint constraint, Object defaultIfNoneFound) { 108 ObjectFinder finder = new ObjectFinder(this, constraint); 109 run(finder); 110 return (finder.foundObject() ? finder.getFoundObject() : defaultIfNoneFound); 111 } 112 113 /** 114 * @return <code>true</code> if the generator is stopped. 115 */ 116 public boolean isStopped() { 117 return this.status == ProcessStatus.STOPPED; 118 } 119 120 /** 121 * @return <code>true</code> if the generator has completed it's task. 122 */ 123 public boolean isFinished() { 124 return this.status == ProcessStatus.COMPLETED; 125 } 126 127 /** 128 * @return <code>true</code> if the generator is still running. 129 */ 130 public boolean isRunning() { 131 return this.status == ProcessStatus.RUNNING; 132 } 133 134 /** 135 * Stop the generator. 136 */ 137 public void stop() throws IllegalStateException { 138 if (this.wrappedGenerator != null) { 139 wrappedGenerator.stop(); 140 } 141 this.status = ProcessStatus.STOPPED; 142 } 143 144 /** 145 * Run a block of code (Closure) until a specific test (Constraint) is 146 * passed. 147 */ 148 public void runUntil(Closure templateCallback, final Constraint constraint) { 149 run(new UntilTrueController(this, templateCallback, constraint)); 150 } 151 152 /** 153 * Reset the ElementGenerator if possible. 154 * 155 * @throws UnsupportedOperationException if this ElementGenerator was a 156 * runOnce instance. 157 */ 158 protected void reset() { 159 if (this.status == ProcessStatus.STOPPED || this.status == ProcessStatus.COMPLETED) { 160 if (this.runOnce) { 161 throw new UnsupportedOperationException("This process template can only safely execute once; " 162 + "instantiate a new instance per request"); 163 } 164 165 this.status = ProcessStatus.RESET; 166 } 167 } 168 169 /** 170 * Set status running. 171 */ 172 protected void setRunning() { 173 this.status = ProcessStatus.RUNNING; 174 } 175 176 /** 177 * Set status completed. 178 */ 179 protected void setCompleted() { 180 this.status = ProcessStatus.COMPLETED; 181 } 182 183 /** 184 * {@inheritDoc} 185 */ 186 public abstract void run(Closure templateCallback); 187 188 /** 189 * When the passed object returns false for the given constraint, the 190 * ElementGenerator will be stopped. Afterwards the {@link #allTrue()} 191 * method can be used to check if all objects passed the test. 192 */ 193 private static class WhileTrueController extends Block { 194 private static final long serialVersionUID = 1L; 195 196 /** 197 * The ElementGenerator that spawns the elements and that will be 198 * stopped when the constraint returns false. 199 */ 200 private ElementGenerator template; 201 202 /** The constraint to test against. */ 203 private Constraint constraint; 204 205 /** 206 * Stores the outcome: true if all handled objects passed the 207 * constraint. 208 */ 209 private boolean allTrue = true; 210 211 /** 212 * Constructor. 213 * 214 * @param template the ElementGenerator that spawns the elements and 215 * that will be stopped if an object fails the constraint. 216 * @param constraint the constraint to test against. 217 */ 218 public WhileTrueController(ElementGenerator template, Constraint constraint) { 219 this.template = template; 220 this.constraint = constraint; 221 } 222 223 /** 224 * {@inheritDoc} 225 */ 226 protected void handle(Object o) { 227 if (!this.constraint.test(o)) { 228 this.allTrue = false; 229 this.template.stop(); 230 } 231 } 232 233 /** 234 * @return <code>true</code> if all objects passed the constraint. 235 */ 236 public boolean allTrue() { 237 return allTrue; 238 } 239 } 240 241 /** 242 * Each passed object will be tested against a Constraint. If returning 243 * true, the ElementGenerator will be stopped. If false, the given Closure 244 * will be executed with the object and the ElementGenerator will continue. 245 */ 246 private static class UntilTrueController extends Block { 247 private static final long serialVersionUID = 1L; 248 249 /** 250 * The ElementGenerator that spawns the element and that will be stopped 251 * when the constraint returns true. 252 */ 253 private ElementGenerator template; 254 255 /** 256 * The closure that has to be executed on all objects until the 257 * constraint returns true. 258 */ 259 private Closure templateCallback; 260 261 /** 262 * The constraint used to test the objects. 263 */ 264 private Constraint constraint; 265 266 /** 267 * Constructor. 268 * 269 * @param template ElementGenerator that spawns the elements and that 270 * will be stopped if the constraint returns true. 271 * @param templateCallback Closure-code executed on each passed object. 272 * @param constraint constraint that will stop the ElementGenerator if 273 * returning true. 274 */ 275 public UntilTrueController(ElementGenerator template, Closure templateCallback, Constraint constraint) { 276 this.template = template; 277 this.templateCallback = templateCallback; 278 this.constraint = constraint; 279 } 280 281 /** 282 * {@inheritDoc} 283 */ 284 protected void handle(Object o) { 285 if (this.constraint.test(o)) { 286 this.template.stop(); 287 } 288 else { 289 this.templateCallback.call(o); 290 } 291 } 292 } 293 294 /** 295 * Will check each passed object against a constraint and call stop if the 296 * constraint returns true. The Object which passed the test will be the 297 * saved for retrieval. 298 */ 299 private static class ObjectFinder extends Block { 300 private static final long serialVersionUID = 1L; 301 302 /** 303 * The generator that spawns the elements and that will be stopped when 304 * the specified element is found. 305 */ 306 private ElementGenerator generator; 307 308 /** Constraint to test against. */ 309 private Constraint constraint; 310 311 /** 312 * The object that passed the test in the constraint or null if not 313 * found. 314 */ 315 private Object foundObject; 316 317 /** 318 * Constructor. 319 * 320 * @param generator the generator which spawns the elements and that 321 * must be stopped when the element is found. 322 * @param constraint the constraint to test against. 323 */ 324 public ObjectFinder(ElementGenerator generator, Constraint constraint) { 325 this.generator = generator; 326 this.constraint = constraint; 327 } 328 329 /** 330 * {@inheritDoc} 331 */ 332 protected void handle(Object o) { 333 if (this.constraint.test(o)) { 334 foundObject = o; 335 generator.stop(); 336 } 337 } 338 339 /** 340 * @return <code>true</code> if the object was found. 341 */ 342 public boolean foundObject() { 343 return foundObject != null; 344 } 345 346 /** 347 * @return the object that complies with the constraint or 348 * <code>null</code> if not found. 349 */ 350 public Object getFoundObject() { 351 return foundObject; 352 } 353 } 354 355 }