001    /*
002     * Copyright 1999-2001,2004 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy 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,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */ 
016    
017    package org.apache.commons.workflow.base;
018    
019    
020    import java.util.ArrayList;
021    import java.util.EmptyStackException;
022    import org.apache.commons.workflow.Activity;
023    import org.apache.commons.workflow.Block;
024    import org.apache.commons.workflow.BlockState;
025    import org.apache.commons.workflow.Context;
026    import org.apache.commons.workflow.Owner;
027    import org.apache.commons.workflow.Step;
028    import org.apache.commons.workflow.StepException;
029    
030    
031    /**
032     * <p><strong>BaseBlock</strong> is a convenient base class for more
033     * sophisticated <code>Block</code> implementations.  It includes management
034     * of the static relationships of nested Steps for this Step (each of which
035     * could conceptually also be a Block and have its own nested Steps).</p>
036     *
037     * @version $Revision: 155475 $ $Date: 2005-02-26 13:31:11 +0000 (Sat, 26 Feb 2005) $
038     * @author Craig R. McClanahan
039     */
040    
041    public abstract class BaseBlock extends DescriptorStep implements Block {
042    
043    
044        // ----------------------------------------------------- Instance Variables
045    
046    
047        /**
048         * The first Step associated with this Block.
049         */
050        protected Step firstStep = null;
051    
052    
053        /**
054         * The last Step associated with this Block.
055         */
056        protected Step lastStep = null;
057    
058    
059        // ------------------------------------------------------------- Properties
060    
061    
062        /**
063         * Return the first Step associated with this Block.
064         */
065        public Step getFirstStep() {
066    
067            return (this.firstStep);
068    
069        }
070    
071    
072        /**
073         * Return the last Step associated with this Activity.
074         */
075        public Step getLastStep() {
076    
077            return (this.lastStep);
078    
079        }
080    
081    
082        // ---------------------------------------------------------- Owner Methods
083    
084    
085        /**
086         * <p>Add a new Step to the end of the sequence of Steps associated with
087         * this Block.</p>
088         *
089         * <p><strong>IMPLEMENTATION NOTE</strong> - The last nested Step is looped
090         * back to the owning Block in order to support the execution flow of
091         * control required by our BaseContext.</p>
092         *
093         * @param step The new step to be added
094         */
095        public void addStep(Step step) {
096    
097            step.setOwner(this);
098            if (firstStep == null) {
099                step.setPreviousStep(null);
100                step.setNextStep(this);
101                firstStep = step;
102                lastStep = step;
103            } else {
104                step.setPreviousStep(lastStep);
105                step.setNextStep(this);
106                lastStep.setNextStep(step);
107                lastStep = step;
108            }
109    
110        }
111    
112    
113        /**
114         * Clear any existing Steps associated with this Block.
115         */
116        public void clearSteps() {
117    
118            Step current = firstStep;
119            while ((current != null) && (current != this)) {
120                Step next = current.getNextStep();
121                if (current instanceof Block)
122                    ((Block) current).clearSteps();
123                current.setOwner(null);
124                current.setPreviousStep(null);
125                current.setNextStep(null);
126                current = next;
127            }
128            firstStep = null;
129            lastStep = null;
130    
131        }
132    
133    
134        /**
135         * Return the identified Step from this Block, if it exists.
136         * Otherwise, return <code>null</code>.
137         *
138         * @param id Identifier of the desired Step
139         */
140        public Step findStep(String id) {
141    
142            Step currentStep = getFirstStep();
143            while (currentStep != null) {
144                if (id.equals(currentStep.getId()))
145                    return (currentStep);
146                if (currentStep == lastStep)
147                    break;
148                currentStep = currentStep.getNextStep();
149            }
150            return (null);
151    
152        }
153    
154    
155        /**
156         * Return the set of Steps associated with this Block.
157         */
158        public Step[] getSteps() {
159    
160            ArrayList list = new ArrayList();
161            Step currentStep = firstStep;
162            while (currentStep != null) {
163                list.add(currentStep);
164                if (currentStep == lastStep)
165                    break;
166                currentStep = currentStep.getNextStep();
167            }
168            Step steps[] = new Step[list.size()];
169            return ((Step[]) list.toArray(steps));
170    
171        }
172    
173    
174        /**
175         * Set the set of Steps associated with this Block, replacing any
176         * existing ones.
177         *
178         * @param steps The new set of steps.
179         */
180        public void setSteps(Step steps[]) {
181    
182            clearSteps();
183            for (int i = 0; i < steps.length; i++)
184                addStep(steps[i]);
185    
186        }
187    
188    
189        // --------------------------------------------------------- Public Methods
190    
191    
192        /**
193         * Perform the executable actions related to this Step, in the context of
194         * the specified Context.
195         *
196         * @param context The Context that is tracking our execution state
197         *
198         * @exception StepException if a processing error has occurred
199         */
200        public void execute(Context context) throws StepException {
201    
202            BlockState state = state(context);
203            if (state == null)
204                initial(context);
205            else
206                subsequent(context, state);
207    
208        }
209    
210    
211        // ------------------------------------------------------ Protected Methods
212    
213    
214        /**
215         * <p>Evaluate the condition specified by the Descriptors associated with
216         * this Block, and return the resulting boolean value.  The default
217         * implementation returns <code>false</code> unconditionally.</p>
218         *
219         * @param context Context within which to evaluate the descriptors
220         */
221        protected boolean evaluate(Context context) {
222    
223            return (false);
224    
225        }
226    
227    
228        /**
229         * <p>Process the initial entry into this Block.  The default
230         * implementation unconditionally skips the nested Steps.</p>
231         *
232         * @param context Context within which to evaluate the condition
233         */
234        protected void initial(Context context) {
235    
236            context.setNextStep(getNextStep());
237    
238        }
239    
240    
241        /**
242         * <p>Peek at the top <code>BlockState</code> element on the stack
243         * maintained by our <code>Context</code>, and return it.  If there
244         * is no such top element, return <code>null</code> instead.</p>
245         *
246         * @param context Context within which to evaluate the current BlockState
247         */
248        protected BlockState state(Context context) {
249    
250            try {
251                BlockState state = context.peekBlockState();
252                if (this == state.getBlock())
253                    return (state);
254            } catch (EmptyStackException e) {
255                ;
256            }
257            return (null);
258    
259        }
260    
261    
262        /**
263         * <p>Process the return from nested execution of the Steps associated
264         * with this Block.  The default implementation unconditionally
265         * proceeds to the next Step at the current nesting level, without
266         * iterating again.</p>
267         *
268         * @param context Context within which to evaluate the condition
269         * @param state BlockState for our block
270         */
271        protected void subsequent(Context context, BlockState state) {
272    
273            context.popBlockState();
274            context.setNextStep(getNextStep());
275    
276        }
277    
278    
279    }