1 /* 2 * Copyright 1999-2001,2004 The Apache Software Foundation. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.apache.commons.workflow.base; 18 19 20 import java.util.ArrayList; 21 import java.util.EmptyStackException; 22 import org.apache.commons.workflow.Activity; 23 import org.apache.commons.workflow.Block; 24 import org.apache.commons.workflow.BlockState; 25 import org.apache.commons.workflow.Context; 26 import org.apache.commons.workflow.Owner; 27 import org.apache.commons.workflow.Step; 28 import org.apache.commons.workflow.StepException; 29 30 31 /** 32 * <p><strong>BaseBlock</strong> is a convenient base class for more 33 * sophisticated <code>Block</code> implementations. It includes management 34 * of the static relationships of nested Steps for this Step (each of which 35 * could conceptually also be a Block and have its own nested Steps).</p> 36 * 37 * @version $Revision: 155475 $ $Date: 2005-02-26 13:31:11 +0000 (Sat, 26 Feb 2005) $ 38 * @author Craig R. McClanahan 39 */ 40 41 public abstract class BaseBlock extends DescriptorStep implements Block { 42 43 44 // ----------------------------------------------------- Instance Variables 45 46 47 /** 48 * The first Step associated with this Block. 49 */ 50 protected Step firstStep = null; 51 52 53 /** 54 * The last Step associated with this Block. 55 */ 56 protected Step lastStep = null; 57 58 59 // ------------------------------------------------------------- Properties 60 61 62 /** 63 * Return the first Step associated with this Block. 64 */ 65 public Step getFirstStep() { 66 67 return (this.firstStep); 68 69 } 70 71 72 /** 73 * Return the last Step associated with this Activity. 74 */ 75 public Step getLastStep() { 76 77 return (this.lastStep); 78 79 } 80 81 82 // ---------------------------------------------------------- Owner Methods 83 84 85 /** 86 * <p>Add a new Step to the end of the sequence of Steps associated with 87 * this Block.</p> 88 * 89 * <p><strong>IMPLEMENTATION NOTE</strong> - The last nested Step is looped 90 * back to the owning Block in order to support the execution flow of 91 * control required by our BaseContext.</p> 92 * 93 * @param step The new step to be added 94 */ 95 public void addStep(Step step) { 96 97 step.setOwner(this); 98 if (firstStep == null) { 99 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 }