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 }