Coverage Report - org.apache.commons.workflow.base.BaseContext
 
Classes in this File Line Coverage Branch Coverage Complexity
BaseContext
0%
0/155
0%
0/66
2.27
 
 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.EmptyStackException;
 21  
 import org.apache.commons.collections.ArrayStack;
 22  
 import org.apache.commons.jxpath.JXPathContext;
 23  
 import org.apache.commons.workflow.Activity;
 24  
 import org.apache.commons.workflow.Block;
 25  
 import org.apache.commons.workflow.BlockState;
 26  
 import org.apache.commons.workflow.Context;
 27  
 import org.apache.commons.workflow.ContextEvent;
 28  
 import org.apache.commons.workflow.ContextListener;
 29  
 import org.apache.commons.workflow.Owner;
 30  
 import org.apache.commons.workflow.Step;
 31  
 import org.apache.commons.workflow.StepException;
 32  
 import org.apache.commons.workflow.Scope;
 33  
 import org.apache.commons.workflow.util.ContextSupport;
 34  
 
 35  
 
 36  
 /**
 37  
  * <strong>BaseContext</strong> is a basic <code>Context</code> implementation
 38  
  * that can serve as a convenient base class for more sophisticated
 39  
  * <code>Context</code> implementations.
 40  
  *
 41  
  * <p><strong>WARNING</strong> - No synchronization is performed within this
 42  
  * class.  If it is used in a multiple thread environment, callers must
 43  
  * take suitable precations.</p>
 44  
  *
 45  
  * @version $Revision: 155475 $ $Date: 2005-02-26 13:31:11 +0000 (Sat, 26 Feb 2005) $
 46  
  * @author Craig R. McClanahan
 47  
  */
 48  
 
 49  
 public class BaseContext implements Context {
 50  
 
 51  
 
 52  
 
 53  
     // ----------------------------------------------------------- Constructors
 54  
 
 55  
 
 56  
     /**
 57  
      * Construct a new BaseContext with all default values.
 58  
      */
 59  
     public BaseContext() {
 60  
 
 61  0
         super();
 62  0
         names[LOCAL_SCOPE] = "local";
 63  0
         scopes[LOCAL_SCOPE] = new BaseScope();
 64  
 
 65  0
     }
 66  
 
 67  
 
 68  
     // ----------------------------------------------------- Instance Variables
 69  
 
 70  
 
 71  
     /**
 72  
      * The <code>Activity</code> that we are associated with and executing
 73  
      * <code>Steps</code> from (if any).
 74  
      */
 75  0
     protected Activity activity = null;
 76  
 
 77  
 
 78  
     /**
 79  
      * The bean representing the information about this Context made visible
 80  
      * through JXPath.
 81  
      */
 82  0
     protected BaseContextBean bean = null;
 83  
 
 84  
 
 85  
     /**
 86  
      * The suspended "next step" Step for each in-progress Activity that has
 87  
      * issued a <code>call()</code> to execute a subordinate Activity.
 88  
      */
 89  0
     protected ArrayStack calls = new ArrayStack();
 90  
 
 91  
 
 92  
     /**
 93  
      * The set of names associated with the registered <code>Scopes</code>.
 94  
      */
 95  0
     protected String names[] = new String[MAX_SCOPES];
 96  
 
 97  
 
 98  
     /**
 99  
      * The <code>Step</code> that will be executed first the next time that
 100  
      * <code>execute()</code> is called.  This is maintained dynamically as
 101  
      * execution proceeds, much like the "next instruction pointer" of a
 102  
      * CPU tracks what instruction will be executed next.
 103  
      */
 104  0
     protected Step nextStep = null;
 105  
 
 106  
 
 107  
     /**
 108  
      * The set of <code>Scopes</code> that have been associated with
 109  
      * this Context.  When initially created, every Context has a
 110  
      * Scope attached to identifier LOCAL_SCOPE already created.
 111  
      */
 112  0
     protected Scope scopes[] = new Scope[MAX_SCOPES];
 113  
 
 114  
 
 115  
     /**
 116  
      * The evaluation stack of nameless objects being processed.
 117  
      */
 118  0
     protected ArrayStack stack = new ArrayStack();
 119  
 
 120  
 
 121  
     /**
 122  
      * The BlockState stack of BlockState objects used to manage iteration.
 123  
      */
 124  0
     protected ArrayStack state = new ArrayStack();
 125  
 
 126  
 
 127  
     /**
 128  
      * The event listener support object for this <code>Context</code>.
 129  
      */
 130  0
     protected ContextSupport support = new ContextSupport(this);
 131  
 
 132  
 
 133  
     /**
 134  
      * The suspension flag.  If this is set when a particular <code>Step</code>
 135  
      * returns, control will be returned from our <code>execute()</code>
 136  
      * method to allow interaction with the rest of the application.  The
 137  
      * next time that <code>execute()</code> is called, execution will
 138  
      * resume with the next scheduled step.
 139  
      */
 140  0
     protected boolean suspend = false;
 141  
 
 142  
 
 143  
     // --------------------------------------------------- Scope Access Methods
 144  
 
 145  
 
 146  
     // These methods provide Steps with the ability to get and set arbitrary
 147  
     // objects in arbitrary scopes.  When a scope is not mentioned explicitly,
 148  
     // either LOCAL_SCOPE will be used, or scopes will be searched in
 149  
     // increasing order of their identifiers, as specified in the various
 150  
     // method descriptions.
 151  
 
 152  
 
 153  
     /**
 154  
      * Return true if a bean with the specified key exist in any specified
 155  
      * scope.  Scopes will be searched in ascending order of their identifiers,
 156  
      * starting with LOCAL_SCOPE.
 157  
      *
 158  
      * @param key Key of the bean to be searched for (cannot be null)
 159  
      */
 160  
     public boolean contains(String key) {
 161  
 
 162  0
         for (int i = 0; i < MAX_SCOPES; i++) {
 163  0
             if (scopes[i] == null)
 164  0
                 continue;
 165  0
             if (scopes[i].containsKey(key))
 166  0
                 return (true);
 167  
         }
 168  0
         return (false);
 169  
 
 170  
     }
 171  
 
 172  
 
 173  
     /**
 174  
      * Does a bean with the specified key exist in the specified scope?
 175  
      *
 176  
      * @param key Key of the bean to be searched for (cannot be null)
 177  
      * @param scope Identifier of the scope to be returned.
 178  
      */
 179  
     public boolean contains(String key, int scope) {
 180  
 
 181  0
         if (scopes[scope] == null)
 182  0
             return (false);
 183  
         else
 184  0
             return (scopes[scope].containsKey(key));
 185  
 
 186  
     }
 187  
 
 188  
 
 189  
     /**
 190  
      * Return the bean associated with the specified key, if it exists in
 191  
      * any scope.  Scopes will be searched in increasing order of their
 192  
      * identifiers, starting with LOCAL_SCOPE.  If the underlying Scope
 193  
      * allows null values, a <code>null</code> return will be ambiguous.
 194  
      * Therefore, you can use the <code>contains()</code> method to determine
 195  
      * whether the key actually exists.
 196  
      *
 197  
      * @param key Key of the bean to be retrieved (cannot be null)
 198  
      */
 199  
     public Object get(String key) {
 200  
 
 201  0
         for (int i = 0; i < MAX_SCOPES; i++) {
 202  0
             if (scopes[i] == null)
 203  0
                 continue;
 204  0
             Object bean = scopes[i].get(key);
 205  0
             if (bean != null)
 206  0
                 return (bean);
 207  
         }
 208  0
         return (null);
 209  
 
 210  
     }
 211  
 
 212  
 
 213  
     /**
 214  
      * Return the bean associated with the specified key, if it exists in
 215  
      * the specified scope.  If the underlying Scope allows null values,
 216  
      * a <code>null</code> return will be ambiguous.  Therefore, you can
 217  
      * use the <code>contains()</code> method to determine whether the
 218  
      * key actually exists.
 219  
      *
 220  
      * @param key Key of the bean to be searched for
 221  
      * @param scope Identifier of the scope to be searched
 222  
      */
 223  
     public Object get(String key, int scope) {
 224  
 
 225  0
         if (scopes[scope] == null)
 226  0
             return (null);
 227  
         else
 228  0
             return (scopes[scope].get(key));
 229  
 
 230  
     }
 231  
 
 232  
 
 233  
     /**
 234  
      * Store the specified bean under the specified key, in local scope,
 235  
      * replacing any previous value for that key.  Any previous value will
 236  
      * be returned.
 237  
      *
 238  
      * @param key Key of the bean to be stored (cannot be null)
 239  
      * @param value Value of the bean to be stored
 240  
      */
 241  
     public Object put(String key, Object value) {
 242  
 
 243  0
         return (scopes[LOCAL_SCOPE].put(key, value));
 244  
 
 245  
     }
 246  
 
 247  
 
 248  
     /**
 249  
      * Store the specified bean under the specified key, in the specified
 250  
      * scope, replacing any previous value for that key.  Any previous value
 251  
      * will be returned.
 252  
      *
 253  
      * @param key Key of the bean to be stored (cannot be null)
 254  
      * @param value Value of the bean to be stored
 255  
      * @param scope Identifier of the scope to use for storage
 256  
      */
 257  
     public Object put(String key, Object value, int scope) {
 258  
 
 259  0
         if (scopes[scope] == null)
 260  0
             return (null);
 261  
         else
 262  0
             return (scopes[scope].put(key, value));
 263  
 
 264  
     }
 265  
 
 266  
 
 267  
     /**
 268  
      * Remove any existing value for the specified key, in any scope.
 269  
      * Scopes will be searched in ascending order of their identifiers,
 270  
      * starting with LOCAL_SCOPE.  Any previous value for this key will
 271  
      * be returned.
 272  
      *
 273  
      * @param key Key of the bean to be removed
 274  
      */
 275  
     public Object remove(String key) {
 276  
 
 277  0
         return (scopes[LOCAL_SCOPE].remove(key));
 278  
 
 279  
     }
 280  
 
 281  
 
 282  
     /**
 283  
      * Remove any existing value for the specified key, from the specified
 284  
      * scope.  Any previous value for this key will be returned.
 285  
      *
 286  
      * @param key Key of the bean to be removed
 287  
      * @param scope Scope the bean to be removed from
 288  
      */
 289  
     public Object remove(String key, int scope) {
 290  
 
 291  0
         if (scopes[scope] == null)
 292  0
             return (null);
 293  
         else
 294  0
             return (scopes[scope].remove(key));
 295  
 
 296  
     }
 297  
 
 298  
 
 299  
 
 300  
     // --------------------------------------------- Scope Registration Methods
 301  
 
 302  
 
 303  
     // These methods provide capabilities to register and retrieve
 304  
     // Scope implementations.  Workflow engine implementations will
 305  
     // register desired scopes when they create Context instances.
 306  
 
 307  
 
 308  
     /**
 309  
      * <p>Register a Scope implementation under the specified identifier.
 310  
      * It is not legal to replace the LOCAL_SCOPE implementation that is
 311  
      * provided by the Context implementation.</p>
 312  
      *
 313  
      * <p>In addition to registering the new Scope such that it can be
 314  
      * accessed dirctly via calls like <code>Context.get(String,int)</code>,
 315  
      * the Scope <code>impl</code> object will also be added to the LOCAL
 316  
      * Scope under the same name.  This makes possible a single unified
 317  
      * namespace of all accessible objects that can be navigated by
 318  
      * expression languages.</p>
 319  
      *
 320  
      * @param scope Scope identifier to register under
 321  
      * @param name Scope name to register under
 322  
      * @param impl Scope implementation to be registered (or null to
 323  
      *  remove a previous registration)
 324  
      * @exception IllegalArgumentException if you attempt to register
 325  
      *  or deregister the local scope
 326  
      */
 327  
     public void addScope(int scope, String name, Scope impl) {
 328  
 
 329  0
         if (scope == LOCAL_SCOPE)
 330  0
             throw new IllegalArgumentException("Cannot replace local scope");
 331  0
         if (impl == null) {
 332  0
             getScope(LOCAL_SCOPE).remove(name);
 333  0
             names[scope] = null;
 334  0
             scopes[scope] = null;
 335  
         } else {
 336  0
             getScope(LOCAL_SCOPE).put(name, impl);
 337  0
             names[scope] = name;
 338  0
             scopes[scope] = impl;
 339  
         }
 340  0
         bean = null;
 341  
 
 342  0
     }
 343  
 
 344  
 
 345  
     /**
 346  
      * Return the Scope instance for our local Scope as a simple property.
 347  
      */
 348  
     public Scope getLocal() {
 349  
 
 350  0
         return (getScope(LOCAL_SCOPE));
 351  
 
 352  
     }
 353  
 
 354  
 
 355  
     /**
 356  
      * Return the Scope implementation registered for the specified
 357  
      * identifier, if any; otherwise, return <code>null</code>.
 358  
      *
 359  
      * @param scope Scope identifier to select
 360  
      */
 361  
     public Scope getScope(int scope) {
 362  
 
 363  0
         if ((scope >= 0) && (scope < MAX_SCOPES))
 364  0
             return (scopes[scope]);
 365  
         else
 366  0
             return (null);
 367  
 
 368  
     }
 369  
 
 370  
 
 371  
     /**
 372  
      * Return the Scope implementation registered for the specified
 373  
      * name, if any; otherwise, return <code>null</code>.
 374  
      *
 375  
      * @param name Scope name to select
 376  
      */
 377  
     public Scope getScope(String name) {
 378  
 
 379  0
         return (getScope(getScopeId(name)));
 380  
 
 381  
     }
 382  
 
 383  
 
 384  
     /**
 385  
      * Return the Scope identifier registered for the specified
 386  
      * name, if any; otherwise, return <code>-1</code>.
 387  
      *
 388  
      * @param name Scope name to select
 389  
      */
 390  
     public int getScopeId(String name) {
 391  
 
 392  0
         for (int i = 0; i < names.length; i++) {
 393  0
             if (names[i] == null)
 394  0
                 continue;
 395  0
             if (names[i].equals(name))
 396  0
                 return (i);
 397  
         }
 398  0
         return (-1);
 399  
 
 400  
     }
 401  
 
 402  
 
 403  
     // ----------------------------------------------- Evaluation Stack Methods
 404  
 
 405  
 
 406  
     /**
 407  
      * Clear the evaluation stack.
 408  
      */
 409  
     public void clear() {
 410  
 
 411  0
         stack.clear();
 412  
 
 413  0
     }
 414  
 
 415  
 
 416  
     /**
 417  
      * Is the evaluation stack currently empty?
 418  
      */
 419  
     public boolean isEmpty() {
 420  
 
 421  0
         return (stack.size() == 0);
 422  
 
 423  
     }
 424  
 
 425  
 
 426  
     /**
 427  
      * Return the top item from the evaluation stack without removing it.
 428  
      *
 429  
      * @exception EmptyStackException if the stack is empty
 430  
      */
 431  
     public Object peek() throws EmptyStackException {
 432  
 
 433  0
         return (stack.peek());
 434  
 
 435  
     }
 436  
 
 437  
 
 438  
     /**
 439  
      * Pop and return the top item from the evaluation stack.
 440  
      *
 441  
      * @exception EmptyStackException if the stack is empty
 442  
      */
 443  
     public Object pop() throws EmptyStackException {
 444  
 
 445  0
         return (stack.pop());
 446  
 
 447  
     }
 448  
 
 449  
 
 450  
     /**
 451  
      * Push a new item onto the top of the evaluation stack.
 452  
      *
 453  
      * @param item New item to be pushed
 454  
      */
 455  
     public void push(Object item) {
 456  
 
 457  0
         stack.push(item);
 458  
 
 459  0
     }
 460  
 
 461  
 
 462  
     // ----------------------------------------------- BlockState Stack Methods
 463  
 
 464  
 
 465  
     /**
 466  
      * Clear the BlockState stack.
 467  
      */
 468  
     public void clearBlockState() {
 469  
 
 470  0
         state.clear();
 471  
 
 472  0
     }
 473  
 
 474  
 
 475  
     /**
 476  
      * Is the BlockState stack currently empty?
 477  
      */
 478  
     public boolean isEmptyBlockState() {
 479  
 
 480  0
         return (state.size() == 0);
 481  
 
 482  
     }
 483  
 
 484  
 
 485  
     /**
 486  
      * Return the top item from the BlockState stack without removing it.
 487  
      *
 488  
      * @exception EmptyStackException if the stack is empty
 489  
      */
 490  
     public BlockState peekBlockState() throws EmptyStackException {
 491  
 
 492  0
         return ((BlockState) state.peek());
 493  
 
 494  
     }
 495  
 
 496  
 
 497  
     /**
 498  
      * Pop and return the top item from the BlockState stack.
 499  
      *
 500  
      * @exception EmptyStackException if the stack is empty
 501  
      */
 502  
     public BlockState popBlockState() throws EmptyStackException {
 503  
 
 504  0
         return ((BlockState) state.pop());
 505  
 
 506  
     }
 507  
 
 508  
 
 509  
     /**
 510  
      * Push a new item onto the top of the BlockState stack.
 511  
      *
 512  
      * @param item New item to be pushed
 513  
      */
 514  
     public void pushBlockState(BlockState item) {
 515  
 
 516  0
         state.push(item);
 517  
 
 518  0
     }
 519  
 
 520  
 
 521  
     // ---------------------------------------------- Dynamic Execution Methods
 522  
 
 523  
 
 524  
     // These methods are used to request the dynamic execution of the
 525  
     // Steps related to a particular Activity.
 526  
 
 527  
 
 528  
     /**
 529  
      * <p>Save the execution state (ie the currently assigned next step)
 530  
      * of the Activity we are currently executing, and begin executing the
 531  
      * specified Activity.  When that Activity exits (either normally
 532  
      * or by throwing an exception), the previous Activity will be resumed
 533  
      * where it left off.
 534  
      *
 535  
      * @param activity The Activity to be called
 536  
      */
 537  
     public void call(Activity activity) {
 538  
 
 539  
         // Save the next Step for the current Activity (if we have
 540  
         // any remaining steps to worry about -- a call on the last
 541  
         // step of an activity is more like a non-local goto)
 542  0
         if (this.nextStep != null)
 543  0
             calls.push(this.nextStep);
 544  
 
 545  
         // Forward control to the first Step of the new Activity
 546  0
         this.activity = activity;
 547  0
         this.nextStep = activity.getFirstStep();
 548  
 
 549  0
     }
 550  
 
 551  
 
 552  
     /**
 553  
      * <p>Execute the <code>Step</code> currently identified as the next
 554  
      * step, and continue execution until there is no next step, or until
 555  
      * the <code>suspend</code> property has been set to true.  Upon
 556  
      * completion of an activity, any execution state that was saved to due
 557  
      * to utilizing the <code>call()</code> method will be restored, and
 558  
      * the saved Activity execution shall be resumed.
 559  
      *
 560  
      * @exception StepException if an exception is thrown by the
 561  
      *  <code>execute()</code> method of a Step we have executed
 562  
      * @exception IllegalStateException if there is no defined next step
 563  
      *  (either because there is no Activity, or because we have already
 564  
      *  completed all the steps of this activity)
 565  
      */
 566  
     public void execute() throws StepException {
 567  
 
 568  
         // Do we actually have a next step to be performed
 569  0
         if (activity == null)
 570  0
             throw new IllegalStateException("No Activity has been selected");
 571  0
         if (nextStep == null)
 572  0
             nextStep = activity.getFirstStep();
 573  0
         if (nextStep == null)
 574  0
             throw new IllegalStateException("Activity has been completed");
 575  
 
 576  
         // Reset the suspend flag until set by another step
 577  0
         suspend = false;
 578  
 
 579  
         // Send a beforeActivity() event to interested listeners
 580  0
         support.fireBeforeActivity(nextStep);
 581  
 
 582  
         // Perform execution until suspended or completed
 583  0
         Step thisStep = null;
 584  0
         StepException exception = null;
 585  
         while (true) {
 586  
 
 587  
             // Process a suspension of Activity execution
 588  0
             if (suspend)
 589  0
                 break;                // Suspend set by a Step
 590  
 
 591  
             // Process completion of an Activity
 592  0
             if (nextStep == null) {
 593  
 
 594  
                 // If there are no active calls, we are done
 595  0
                 if (calls.empty())
 596  0
                     break;
 597  
 
 598  
                 // If there are active calls, resume the most recent one
 599  
                 try {
 600  0
                     nextStep = (Step) calls.pop();
 601  0
                     Owner owner = nextStep.getOwner();
 602  0
                     while (!(owner instanceof Activity)) {
 603  0
                         owner = ((Step) owner).getOwner();
 604  
                     }
 605  0
                     this.activity = (Activity) owner;
 606  0
                 } catch (EmptyStackException e) {
 607  
                     ; // Can not happen
 608  0
                 }
 609  0
                 continue;
 610  
 
 611  
             }
 612  
 
 613  
             // Execute the (now) current Step
 614  0
             thisStep = nextStep;
 615  0
             nextStep = thisStep.getNextStep(); // Assume sequential execution
 616  
             try {
 617  0
                 support.fireBeforeStep(thisStep);
 618  0
                 thisStep.execute(this);
 619  0
                 support.fireAfterStep(thisStep);
 620  0
             } catch (StepException e) {
 621  0
                 exception = e;
 622  0
                 support.fireAfterStep(thisStep, exception);
 623  0
                 break;
 624  0
             } catch (Throwable t) {
 625  0
                 exception = new StepException(t, thisStep);
 626  0
                 support.fireAfterStep(thisStep, exception);
 627  0
                 break;
 628  0
             }
 629  
 
 630  
         }
 631  
 
 632  
         // Send an afterActivity event to interested listeners
 633  0
         support.fireAfterActivity(thisStep, exception);
 634  
 
 635  
         // Rethrow any StepException that was thrown
 636  0
         if (exception != null)
 637  0
             throw exception;
 638  
 
 639  0
     }
 640  
 
 641  
 
 642  
     /**
 643  
      * <p>Return the <code>Activity</code> we will be executing when the
 644  
      * <code>execute()</code> method is called, if any.</p>
 645  
      */
 646  
     public Activity getActivity() {
 647  
 
 648  0
         return (this.activity);
 649  
 
 650  
     }
 651  
 
 652  
 
 653  
     /**
 654  
      * Return the set of pending Step executions that are pending because
 655  
      * of calls to subordinate Activities have occurred.  If there are
 656  
      * no pending Step executions, a zero-length array is returned.
 657  
      */
 658  
     public Step[] getCalls() {
 659  
 
 660  0
         Step steps[] = new Step[calls.size()];
 661  0
         return ((Step[]) calls.toArray(steps));
 662  
 
 663  
     }
 664  
 
 665  
 
 666  
     /**
 667  
      * Return the JXPathContext object that represents a unified namespace
 668  
      * covering all of our registered <code>Scopes</code>.
 669  
      */
 670  
     public JXPathContext getJXPathContext() {
 671  
 
 672  0
         if (bean == null)
 673  0
             bean = new BaseContextBean(this);
 674  0
         return (JXPathContext.newContext(bean));
 675  
 
 676  
     }
 677  
 
 678  
 
 679  
     /**
 680  
      * <p>Return the <code>Step</code> that will be executed the next time
 681  
      * that <code>execute()</code> is called, if any.</p>
 682  
      */
 683  
     public Step getNextStep() {
 684  
 
 685  0
         return (this.nextStep);
 686  
 
 687  
     }
 688  
 
 689  
 
 690  
     /**
 691  
      * <p>Return the suspend flag.</p>
 692  
      */
 693  
     public boolean getSuspend() {
 694  
 
 695  0
         return (this.suspend);
 696  
 
 697  
     }
 698  
 
 699  
 
 700  
     /**
 701  
      * <p>Set the <code>Activity</code> to be executed, and make the first
 702  
      * defined <code>Step</code> within this <code>Activity</code> the next
 703  
      * action to be performed by <code>execute()</code>.</p>
 704  
      *
 705  
      * <p>If <code>null</code> is passed, any currently associated Activity
 706  
      * will be released, and the evaluation stack and nested call state
 707  
      * stack will be cleared.</p>
 708  
      *
 709  
      * <p><strong>WARNING</strong> - This will have to become more sophisticated
 710  
      * in order to support calling nested Activities.</p>
 711  
      *
 712  
      * @param activity The new Activity to be executed, or <code>null</code>
 713  
      *  to release resources
 714  
      */
 715  
     public void setActivity(Activity activity) {
 716  
 
 717  0
         if (activity == null) {
 718  0
             this.activity = null;
 719  0
             this.nextStep = null;
 720  0
             clear();
 721  0
             clearBlockState();
 722  
         } else {
 723  0
             this.activity = activity;
 724  0
             this.nextStep = activity.getFirstStep();
 725  
         }
 726  0
         calls.clear();
 727  
 
 728  0
     }
 729  
 
 730  
 
 731  
     /**
 732  
      * <p>Set the <code>Step</code> that will be executed the next time
 733  
      * that <code>execute()</code> is called.  This is called by a
 734  
      * <code>Step</code> that wants to perform branching based on some
 735  
      * calculated results.</p>
 736  
      *
 737  
      * @param nextStep The next Step to be executed
 738  
      *
 739  
      * @exception IllegalArgumentException if the specified Step is not
 740  
      *  part of the current Activity
 741  
      */
 742  
     public void setNextStep(Step nextStep) {
 743  
 
 744  
         // Make sure the specified next Step is within the current Activity
 745  0
         if (nextStep != null) {
 746  0
             Owner owner = nextStep.getOwner();
 747  0
             while (owner instanceof Block)
 748  0
                 owner = ((Block) owner).getOwner();
 749  0
             if (this.activity != (Activity) owner)
 750  0
                 throw new IllegalArgumentException
 751  
                     ("Step is not part of the current Activity");
 752  
         }
 753  0
         this.nextStep = nextStep;
 754  
 
 755  0
     }
 756  
 
 757  
 
 758  
     /**
 759  
      * <p>Set the suspend flag.  This is called by a <code>Step</code> that
 760  
      * wants to signal the <code>Context</code> to return control to the
 761  
      * caller of the <code>execute()</code> method before executing the
 762  
      * next <code>Step</code> in the current activity.</p>
 763  
      *
 764  
      * @param suspend The new suspend flag
 765  
      */
 766  
     public void setSuspend(boolean suspend) {
 767  
 
 768  0
         this.suspend = suspend;
 769  
 
 770  0
     }
 771  
 
 772  
 
 773  
     // ------------------------------------------------- Event Listener Methods
 774  
 
 775  
 
 776  
     /**
 777  
      * Add a listener that is notified each time beans are added,
 778  
      * replaced, or removed in this context.
 779  
      *
 780  
      * @param listener The ContextListener to be added
 781  
      */
 782  
     public void addContextListener(ContextListener listener) {
 783  
 
 784  0
         support.addContextListener(listener);
 785  
 
 786  0
     }
 787  
 
 788  
 
 789  
     /**
 790  
      * Remove a listener that is notified each time beans are added,
 791  
      * replaced, or removed in this context.
 792  
      *
 793  
      * @param listener The ContextListener to be removed
 794  
      */
 795  
     public void removeContextListener(ContextListener listener) {
 796  
 
 797  0
         support.removeContextListener(listener);
 798  
 
 799  0
     }
 800  
 
 801  
 
 802  
     // -------------------------------------------------------- Package Methods
 803  
 
 804  
 
 805  
     /**
 806  
      * Return the array of registered <code>Scope</code> names for this
 807  
      * <code>Context</code>.
 808  
      */
 809  
     String[] getScopeNames() {
 810  
 
 811  0
         return (this.names);
 812  
 
 813  
     }
 814  
 
 815  
 
 816  
 }