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 }