/** * Copyright (C) 2015 Valkyrie RCP * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.valkyriercp.rules.closure.support; import org.valkyriercp.rules.closure.Closure; import org.valkyriercp.rules.closure.ElementGenerator; import org.valkyriercp.rules.constraint.Constraint; /** * Base superclass for process templates. * * @author Keith Donald */ public abstract class AbstractElementGenerator implements ElementGenerator { /** Wrapping instance for internal usage. */ private ElementGenerator wrappedGenerator; /** <code>true</code> if this ElementGenerator may run once. */ private boolean runOnce = false; /** Current status of this ElementGenerator. */ private volatile ProcessStatus status = ProcessStatus.CREATED; /** * Default constructor. */ protected AbstractElementGenerator() { } /** * Constructor. * * @param runOnce <code>true</code> if this ElementGenerator may only be * run once (will throw an Exception if called more than once). */ protected AbstractElementGenerator(boolean runOnce) { this.runOnce = runOnce; } /** * Create an wrapper instance for internal usage. * * @param wrappedTemplate */ private AbstractElementGenerator(ElementGenerator wrappedTemplate) { this.wrappedGenerator = wrappedTemplate; } /** * @return the wrapped ElementGenerator or <code>null</code>. */ protected ElementGenerator getWrappedTemplate() { return wrappedGenerator; } /** * {@inheritDoc} */ public boolean allTrue(Constraint constraint) { WhileTrueController controller = new WhileTrueController(this, constraint); run(controller); return controller.allTrue(); } /** * {@inheritDoc} */ public boolean anyTrue(Constraint constraint) { return findFirst(constraint, null) != null; } /** * {@inheritDoc} */ public ElementGenerator findAll(final Constraint constraint) { return new AbstractElementGenerator(this) { public void run(final Closure closure) { getWrappedTemplate().run(new IfBlock(constraint, closure)); } }; } /** * {@inheritDoc} */ public Object findFirst(Constraint constraint) { return findFirst(constraint, null); } /** * {@inheritDoc} */ public Object findFirst(Constraint constraint, Object defaultIfNoneFound) { ObjectFinder finder = new ObjectFinder(this, constraint); run(finder); return (finder.foundObject() ? finder.getFoundObject() : defaultIfNoneFound); } /** * @return <code>true</code> if the generator is stopped. */ public boolean isStopped() { return this.status == ProcessStatus.STOPPED; } /** * @return <code>true</code> if the generator has completed it's task. */ public boolean isFinished() { return this.status == ProcessStatus.COMPLETED; } /** * @return <code>true</code> if the generator is still running. */ public boolean isRunning() { return this.status == ProcessStatus.RUNNING; } /** * Stop the generator. */ public void stop() throws IllegalStateException { if (this.wrappedGenerator != null) { wrappedGenerator.stop(); } this.status = ProcessStatus.STOPPED; } /** * Run a block of code (Closure) until a specific test (Constraint) is * passed. */ public void runUntil(Closure templateCallback, final Constraint constraint) { run(new UntilTrueController(this, templateCallback, constraint)); } /** * Reset the ElementGenerator if possible. * * @throws UnsupportedOperationException if this ElementGenerator was a * runOnce instance. */ protected void reset() { if (this.status == ProcessStatus.STOPPED || this.status == ProcessStatus.COMPLETED) { if (this.runOnce) { throw new UnsupportedOperationException("This process template can only safely execute once; " + "instantiate a new instance per request"); } this.status = ProcessStatus.RESET; } } /** * Set status running. */ protected void setRunning() { this.status = ProcessStatus.RUNNING; } /** * Set status completed. */ protected void setCompleted() { this.status = ProcessStatus.COMPLETED; } /** * {@inheritDoc} */ public abstract void run(Closure templateCallback); /** * When the passed object returns false for the given constraint, the * ElementGenerator will be stopped. Afterwards the {@link #allTrue()} * method can be used to check if all objects passed the test. */ private static class WhileTrueController extends Block { private static final long serialVersionUID = 1L; /** * The ElementGenerator that spawns the elements and that will be * stopped when the constraint returns false. */ private ElementGenerator template; /** The constraint to test against. */ private Constraint constraint; /** * Stores the outcome: true if all handled objects passed the * constraint. */ private boolean allTrue = true; /** * Constructor. * * @param template the ElementGenerator that spawns the elements and * that will be stopped if an object fails the constraint. * @param constraint the constraint to test against. */ public WhileTrueController(ElementGenerator template, Constraint constraint) { this.template = template; this.constraint = constraint; } /** * {@inheritDoc} */ protected void handle(Object o) { if (!this.constraint.test(o)) { this.allTrue = false; this.template.stop(); } } /** * @return <code>true</code> if all objects passed the constraint. */ public boolean allTrue() { return allTrue; } } /** * Each passed object will be tested against a Constraint. If returning * true, the ElementGenerator will be stopped. If false, the given Closure * will be executed with the object and the ElementGenerator will continue. */ private static class UntilTrueController extends Block { private static final long serialVersionUID = 1L; /** * The ElementGenerator that spawns the element and that will be stopped * when the constraint returns true. */ private ElementGenerator template; /** * The closure that has to be executed on all objects until the * constraint returns true. */ private Closure templateCallback; /** * The constraint used to test the objects. */ private Constraint constraint; /** * Constructor. * * @param template ElementGenerator that spawns the elements and that * will be stopped if the constraint returns true. * @param templateCallback Closure-code executed on each passed object. * @param constraint constraint that will stop the ElementGenerator if * returning true. */ public UntilTrueController(ElementGenerator template, Closure templateCallback, Constraint constraint) { this.template = template; this.templateCallback = templateCallback; this.constraint = constraint; } /** * {@inheritDoc} */ protected void handle(Object o) { if (this.constraint.test(o)) { this.template.stop(); } else { this.templateCallback.call(o); } } } /** * Will check each passed object against a constraint and call stop if the * constraint returns true. The Object which passed the test will be the * saved for retrieval. */ private static class ObjectFinder extends Block { private static final long serialVersionUID = 1L; /** * The generator that spawns the elements and that will be stopped when * the specified element is found. */ private ElementGenerator generator; /** Constraint to test against. */ private Constraint constraint; /** * The object that passed the test in the constraint or null if not * found. */ private Object foundObject; /** * Constructor. * * @param generator the generator which spawns the elements and that * must be stopped when the element is found. * @param constraint the constraint to test against. */ public ObjectFinder(ElementGenerator generator, Constraint constraint) { this.generator = generator; this.constraint = constraint; } /** * {@inheritDoc} */ protected void handle(Object o) { if (this.constraint.test(o)) { foundObject = o; generator.stop(); } } /** * @return <code>true</code> if the object was found. */ public boolean foundObject() { return foundObject != null; } /** * @return the object that complies with the constraint or * <code>null</code> if not found. */ public Object getFoundObject() { return foundObject; } } }