You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by ra...@apache.org on 2006/05/18 03:16:31 UTC

svn commit: r407424 [1/2] - in /jakarta/commons/proper/scxml/trunk: src/main/java/org/apache/commons/scxml/env/ src/main/java/org/apache/commons/scxml/io/ src/main/java/org/apache/commons/scxml/model/ src/test/java/org/apache/commons/scxml/ src/test/ja...

Author: rahul
Date: Wed May 17 18:16:30 2006
New Revision: 407424

URL: http://svn.apache.org/viewvc?rev=407424&view=rev
Log:
1) External document digester did not process custom actions (fix introduces a backwards incompatible change). Documentation and test cases have been updated. A wiki page has been created to note such changes as we move towards the first RC.
SCXML-8
2) Moved the post document-parsing model updation (and checking) bits to a io package scoped ModelUpdater class
3) Added some more utility methods to SCXMLTestHelper and SCXMLDigester
4) Added examples and test cases of shallow and deep history, and history defaults
5) SCXMLDigester's parsing methods now throw a ModelException (backwards incompatible change). This is one of the ways to provide better error reporting about model errors introduced by reading in a state machine from flawed SCXML documents.


Added:
    jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/io/ModelUpdater.java   (with props)
    jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/model/CustomAction.java   (with props)
    jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world-01.xml
      - copied, changed from r407046, jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world.xml
    jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world-02.xml   (with props)
    jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/external-hello-world.xml   (with props)
    jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-deep-01.xml   (with props)
    jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-default-01.xml   (with props)
    jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-shallow-01.xml   (with props)
Removed:
    jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world.xml
Modified:
    jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/env/AbstractStateMachine.java
    jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/io/SCXMLDigester.java
    jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/SCXMLTestHelper.java
    jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/io/SCXMLDigesterTest.java
    jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/model/CustomActionTest.java
    jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/model/Hello.java
    jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/model/HistoryTest.java
    jakarta/commons/proper/scxml/trunk/xdocs/guide/core-digester.xml
    jakarta/commons/proper/scxml/trunk/xdocs/guide/custom-actions.xml

Modified: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/env/AbstractStateMachine.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/env/AbstractStateMachine.java?rev=407424&r1=407423&r2=407424&view=diff
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/env/AbstractStateMachine.java (original)
+++ jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/env/AbstractStateMachine.java Wed May 17 18:16:30 2006
@@ -129,6 +129,8 @@
                 logError(ioe);
             } catch (SAXException sae) {
                 logError(sae);
+            } catch (ModelException me) {
+                logError(me);
             }
         }
         engine = new SCXMLExecutor(evaluator, new SimpleDispatcher(),

Added: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/io/ModelUpdater.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/io/ModelUpdater.java?rev=407424&view=auto
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/io/ModelUpdater.java (added)
+++ jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/io/ModelUpdater.java Wed May 17 18:16:30 2006
@@ -0,0 +1,309 @@
+/*
+ *
+ *   Copyright 2005-2006 The Apache Software Foundation.
+ *
+ *  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.apache.commons.scxml.io;
+
+import java.text.MessageFormat;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.scxml.SCXMLHelper;
+import org.apache.commons.scxml.model.History;
+import org.apache.commons.scxml.model.Initial;
+import org.apache.commons.scxml.model.ModelException;
+import org.apache.commons.scxml.model.Parallel;
+import org.apache.commons.scxml.model.SCXML;
+import org.apache.commons.scxml.model.State;
+import org.apache.commons.scxml.model.Transition;
+import org.apache.commons.scxml.model.TransitionTarget;
+
+/**
+ * The ModelUpdater provides the utility methods to check the Commons
+ * SCXML model for inconsistencies, detect errors, and wire the Commons
+ * SCXML model appropriately post document parsing by the digester to make
+ * it executor ready.
+ */
+final class ModelUpdater {
+
+    /*
+     * Post-processing methods to make the SCXML object SCXMLExecutor ready.
+     */
+    /**
+     * <p>Update the SCXML object model and make it SCXMLExecutor ready.
+     * This is part of post-digester processing, and sets up the necessary
+     * object references throughtout the SCXML object model for the parsed
+     * document.</p>
+     *
+     * @param scxml The SCXML object (output from Digester)
+     * @throws ModelException If the object model is flawed
+     */
+   static void updateSCXML(final SCXML scxml) throws ModelException {
+       // Watch case, slightly unfortunate naming ;-)
+       String initialstate = scxml.getInitialstate();
+       //we have to use getTargets() here since the initialState can be
+       //an indirect descendant
+       // Concern marked by one of the code reviewers: better type check,
+       //            now ClassCastException happens for Parallel
+       // Response: initial should be a State, for Parallel, it is implicit
+       State initialState = (State) scxml.getTargets().get(initialstate);
+       if (initialState == null) {
+           // Where do we, where do we go?
+           logAndThrowModelError(ERR_SCXML_NO_INIT, new Object[] {
+               initialstate });
+       }
+       scxml.setInitialState(initialState);
+       Map targets = scxml.getTargets();
+       Map states = scxml.getStates();
+       Iterator i = states.keySet().iterator();
+       while (i.hasNext()) {
+           updateState((State) states.get(i.next()), targets);
+       }
+   }
+
+    /**
+      * Update this State object (part of post-digestion processing).
+      * Also checks for any errors in the document.
+      *
+      * @param s The State object
+      * @param targets The global Map of all transition targets
+      * @throws ModelException If the object model is flawed
+      */
+    private static void updateState(final State s, final Map targets)
+    throws ModelException {
+        //ensure both onEntry and onExit have parent
+        //could add next two lines as a Digester rule for OnEntry/OnExit
+        s.getOnEntry().setParent(s);
+        s.getOnExit().setParent(s);
+        //initialize next / inital
+        Initial ini = s.getInitial();
+        Map c = s.getChildren();
+        TransitionTarget initialState = null;
+        if (!c.isEmpty()) {
+            if (ini == null) {
+                logAndThrowModelError(ERR_STATE_NO_INIT,
+                    new Object[] {getStateName(s)});
+            }
+            Transition initialTransition = ini.getTransition();
+            updateTransition(initialTransition, targets);
+            initialState = initialTransition.getTarget();
+            // we have to allow for an indirect descendant initial (targets)
+            //check that initialState is a descendant of s
+            if (initialState == null
+                    || !SCXMLHelper.isDescendant(initialState, s)) {
+                logAndThrowModelError(ERR_STATE_BAD_INIT,
+                    new Object[] {getStateName(s)});
+            }
+        }
+        List histories = s.getHistory();
+        Iterator histIter = histories.iterator();
+        while (histIter.hasNext()) {
+            if (s.isSimple()) {
+                logAndThrowModelError(ERR_HISTORY_SIMPLE_STATE,
+                    new Object[] {getStateName(s)});
+            }
+            History h = (History) histIter.next();
+            Transition historyTransition = h.getTransition();
+            if (historyTransition == null) {
+                // try to assign initial as default
+                if (initialState != null
+                        && !(initialState instanceof History)) {
+                    historyTransition = new Transition();
+                    historyTransition.setNext(initialState.getId());
+                    historyTransition.setParent(h);
+                    h.setTransition(historyTransition);
+                } else {
+                    logAndThrowModelError(ERR_HISTORY_NO_DEFAULT,
+                        new Object[] {h.getId(), getStateName(s)});
+                }
+            }
+            updateTransition(historyTransition, targets);
+            State historyState = (State) historyTransition.getTarget();
+            if (historyState == null) {
+                logAndThrowModelError(ERR_STATE_NO_HIST,
+                    new Object[] {getStateName(s)});
+            }
+            if (!h.isDeep()) {
+                if (!c.containsValue(historyState)) {
+                    logAndThrowModelError(ERR_STATE_BAD_SHALLOW_HIST,
+                        new Object[] {getStateName(s)});
+                }
+            } else {
+                if (!SCXMLHelper.isDescendant(historyState, s)) {
+                    logAndThrowModelError(ERR_STATE_BAD_DEEP_HIST,
+                        new Object[] {getStateName(s)});
+                }
+            }
+        }
+        Map t = s.getTransitions();
+        Iterator i = t.keySet().iterator();
+        while (i.hasNext()) {
+            Iterator j = ((List) t.get(i.next())).iterator();
+            while (j.hasNext()) {
+                Transition trn = (Transition) j.next();
+                //could add next two lines as a Digester rule for Transition
+                trn.setParent(s);
+                updateTransition(trn, targets);
+            }
+        }
+        Parallel p = s.getParallel();
+        if (p != null) {
+            updateParallel(p, targets);
+        } else {
+            Iterator j = c.keySet().iterator();
+            while (j.hasNext()) {
+                updateState((State) c.get(j.next()), targets);
+            }
+        }
+    }
+
+    /**
+      * Update this Parallel object (part of post-digestion processing).
+      *
+      * @param p The Parallel object
+      * @param targets The global Map of all transition targets
+      * @throws ModelException If the object model is flawed
+      */
+    private static void updateParallel(final Parallel p, final Map targets)
+    throws ModelException {
+        Iterator i = p.getStates().iterator();
+        while (i.hasNext()) {
+            updateState((State) i.next(), targets);
+        }
+    }
+
+    /**
+      * Update this Transition object (part of post-digestion processing).
+      *
+      * @param t The Transition object
+      * @param targets The global Map of all transition targets
+      * @throws ModelException If the object model is flawed
+      */
+    private static void updateTransition(final Transition t,
+            final Map targets) throws ModelException {
+        String next = t.getNext();
+        TransitionTarget tt = t.getTarget();
+        if (tt == null) {
+            tt = (TransitionTarget) targets.get(next);
+            if (tt == null) {
+                logAndThrowModelError(ERR_TARGET_NOT_FOUND, new Object[] {
+                    next });
+            }
+            t.setTarget(tt);
+        }
+    }
+
+    /**
+      * Log an error discovered in post-digestion processing.
+      *
+      * @param errType The type of error
+      * @param msgArgs The arguments for formatting the error message
+      * @throws ModelException The model error, always thrown.
+      */
+    private static void logAndThrowModelError(final String errType,
+            final Object[] msgArgs) throws ModelException {
+        MessageFormat msgFormat = new MessageFormat(errType);
+        String errMsg = msgFormat.format(msgArgs);
+        org.apache.commons.logging.Log log = LogFactory.
+            getLog(ModelUpdater.class);
+        log.error(errMsg);
+        throw new ModelException(errMsg);
+    }
+
+    /**
+     * Get state identifier for error message. This method is only
+     * called to produce an appropriate log message in some error
+     * conditions.
+     *
+     * @param state The <code>State</code> object
+     * @return The state identifier for the error message
+     */
+    private static String getStateName(final State state) {
+        String badState = "anonymous state";
+        if (!SCXMLHelper.isStringEmpty(state.getId())) {
+            badState = "state with ID \"" + state.getId() + "\"";
+        }
+        return badState;
+    }
+
+    /**
+     * Discourage instantiation since this is a utility class.
+     */
+    private ModelUpdater() {
+        super();
+    }
+
+    //// Error messages
+    /**
+     * Error message when SCXML document specifies an illegal initial state.
+     */
+    private static final String ERR_SCXML_NO_INIT = "No SCXML child state "
+        + "with ID \"{0}\" found; illegal initialstate for SCXML document";
+
+    /**
+     * Error message when a state element specifies an initial state which
+     * cannot be found.
+     */
+    private static final String ERR_STATE_NO_INIT = "No initial element "
+        + "available for {0}";
+
+    /**
+     * Error message when a state element specifies an initial state which
+     * is not a direct descendent.
+     */
+    private static final String ERR_STATE_BAD_INIT = "Initial state "
+        + "null or not a descendant of {0}";
+
+    /**
+     * Error message when a referenced history state cannot be found.
+     */
+    private static final String ERR_STATE_NO_HIST = "Referenced history state"
+        + " null for {0}";
+
+    /**
+     * Error message when a shallow history state is not a child state.
+     */
+    private static final String ERR_STATE_BAD_SHALLOW_HIST = "History state"
+        + " for shallow history is not child for {0}";
+
+    /**
+     * Error message when a deep history state is not a descendent state.
+     */
+    private static final String ERR_STATE_BAD_DEEP_HIST = "History state"
+        + " for deep history is not descendant for {0}";
+
+    /**
+     * Transition target is not a legal IDREF (not found).
+     */
+    private static final String ERR_TARGET_NOT_FOUND =
+        "Transition target with ID \"{0}\" not found";
+
+    /**
+     * Simple states should not contain a history.
+     */
+    private static final String ERR_HISTORY_SIMPLE_STATE =
+        "Simple {0} contains history elements";
+
+    /**
+     * History does not specify a default transition target.
+     */
+    private static final String ERR_HISTORY_NO_DEFAULT =
+        "No default target specified for history with ID \"{0}\""
+        + " belonging to {1}";
+
+}

Propchange: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/io/ModelUpdater.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/io/ModelUpdater.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/io/SCXMLDigester.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/io/SCXMLDigester.java?rev=407424&r1=407423&r2=407424&view=diff
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/io/SCXMLDigester.java (original)
+++ jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/io/SCXMLDigester.java Wed May 17 18:16:30 2006
@@ -20,7 +20,6 @@
 import java.io.IOException;
 import java.net.URL;
 import java.text.MessageFormat;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -43,6 +42,7 @@
 import org.apache.commons.scxml.model.Action;
 import org.apache.commons.scxml.model.Assign;
 import org.apache.commons.scxml.model.Cancel;
+import org.apache.commons.scxml.model.CustomAction;
 import org.apache.commons.scxml.model.Data;
 import org.apache.commons.scxml.model.Datamodel;
 import org.apache.commons.scxml.model.Else;
@@ -54,6 +54,7 @@
 import org.apache.commons.scxml.model.If;
 import org.apache.commons.scxml.model.Initial;
 import org.apache.commons.scxml.model.Log;
+import org.apache.commons.scxml.model.ModelException;
 import org.apache.commons.scxml.model.OnEntry;
 import org.apache.commons.scxml.model.OnExit;
 import org.apache.commons.scxml.model.Parallel;
@@ -90,7 +91,7 @@
      * that is intended to be parsed by this digester <b>must</b>
      * bind the SCXML elements to this namespace.
      */
-    public static final String NAMESPACE_SCXML =
+    private static final String NAMESPACE_SCXML =
         "http://www.w3.org/2005/07/scxml";
 
     //---------------------- PUBLIC METHODS ----------------------//
@@ -107,17 +108,117 @@
      *
      * @throws IOException Underlying Digester parsing threw an IOException
      * @throws SAXException Underlying Digester parsing threw a SAXException
+     * @throws ModelException If the resulting document model has flaws
      *
      * @see ErrorHandler
      * @see PathResolver
      */
     public static SCXML digest(final URL scxmlURL,
             final ErrorHandler errHandler)
-    throws IOException, SAXException {
+    throws IOException, SAXException, ModelException {
+
+        if (scxmlURL == null) {
+            throw new IllegalArgumentException(ERR_NULL_URL);
+        }
+
+        return digest(scxmlURL, errHandler, null);
+
+    }
+
+    /**
+     * <p>API for standalone usage where the SCXML document is a URI.
+     * A PathResolver must be provided.</p>
+     *
+     * @param pathResolver
+     *            The PathResolver for this context
+     * @param documentRealPath
+     *            The String pointing to the absolute (real) path of the
+     *            SCXML document
+     * @param errHandler
+     *            The SAX ErrorHandler
+     *
+     * @return SCXML The SCXML object corresponding to the file argument
+     *
+     * @throws IOException Underlying Digester parsing threw an IOException
+     * @throws SAXException Underlying Digester parsing threw a SAXException
+     * @throws ModelException If the resulting document model has flaws
+     *
+     * @see ErrorHandler
+     * @see PathResolver
+     */
+    public static SCXML digest(final String documentRealPath,
+            final ErrorHandler errHandler, final PathResolver pathResolver)
+    throws IOException, SAXException, ModelException {
+
+        return digest(documentRealPath, errHandler, pathResolver, null);
+
+    }
+
+    /**
+     * <p>API for standalone usage where the SCXML document is an
+     * InputSource. This method may be used when the SCXML document is
+     * packaged in a Java archive, or part of a compound document
+     * where the SCXML root is available as a
+     * <code>org.w3c.dom.Element</code> or via a <code>java.io.Reader</code>.
+     * </p>
+     *
+     * <p><em>Note:</em> Since there is no path resolution, the SCXML document
+     * must not have external state sources.</p>
+     *
+     * @param documentInputSource
+     *            The InputSource for the SCXML document
+     * @param errHandler
+     *            The SAX ErrorHandler
+     *
+     * @return SCXML The SCXML object corresponding to the file argument
+     *
+     * @throws IOException Underlying Digester parsing threw an IOException
+     * @throws SAXException Underlying Digester parsing threw a SAXException
+     * @throws ModelException If the resulting document model has flaws
+     *
+     * @see ErrorHandler
+     */
+    public static SCXML digest(final InputSource documentInputSource,
+            final ErrorHandler errHandler)
+    throws IOException, SAXException, ModelException {
+
+        if (documentInputSource == null) {
+            throw new IllegalArgumentException(ERR_NULL_ISRC);
+        }
+
+        return digest(documentInputSource, errHandler, null);
+
+    }
+
+    /**
+     * <p>API for standalone usage where the SCXML document is a URL, and
+     * the document uses custom actions.</p>
+     *
+     * @param scxmlURL
+     *            a canonical absolute URL to parse (relative URLs within the
+     *            top level document are to be resovled against this URL).
+     * @param errHandler
+     *            The SAX ErrorHandler
+     * @param customActions
+     *            The list of {@link CustomAction}s this digester
+     *            instance will process, can be null or empty
+     *
+     * @return SCXML The SCXML object corresponding to the file argument
+     *
+     * @throws IOException Underlying Digester parsing threw an IOException
+     * @throws SAXException Underlying Digester parsing threw a SAXException
+     * @throws ModelException If the resulting document model has flaws
+     *
+     * @see ErrorHandler
+     * @see PathResolver
+     */
+    public static SCXML digest(final URL scxmlURL,
+            final ErrorHandler errHandler, final List customActions)
+    throws IOException, SAXException, ModelException {
 
         SCXML scxml = null;
         Digester scxmlDigester = SCXMLDigester
-                .newInstance(null, new URLResolver(scxmlURL));
+                .newInstance(null, new URLResolver(scxmlURL), customActions);
         scxmlDigester.setErrorHandler(errHandler);
 
         try {
@@ -136,7 +237,7 @@
         }
 
         if (scxml != null) {
-            updateSCXML(scxml);
+            ModelUpdater.updateSCXML(scxml);
         }
 
         return scxml;
@@ -154,21 +255,31 @@
      *            SCXML document
      * @param errHandler
      *            The SAX ErrorHandler
+     * @param customActions
+     *            The list of {@link CustomAction}s this digester
+     *            instance will process, can be null or empty
      *
      * @return SCXML The SCXML object corresponding to the file argument
      *
      * @throws IOException Underlying Digester parsing threw an IOException
      * @throws SAXException Underlying Digester parsing threw a SAXException
+     * @throws ModelException If the resulting document model has flaws
      *
      * @see ErrorHandler
      * @see PathResolver
      */
     public static SCXML digest(final String documentRealPath,
-            final ErrorHandler errHandler, final PathResolver pathResolver)
-    throws IOException, SAXException {
+            final ErrorHandler errHandler, final PathResolver pathResolver,
+            final List customActions)
+    throws IOException, SAXException, ModelException {
+
+        if (documentRealPath == null) {
+            throw new IllegalArgumentException(ERR_NULL_PATH);
+        }
 
         SCXML scxml = null;
-        Digester scxmlDigester = SCXMLDigester.newInstance(null, pathResolver);
+        Digester scxmlDigester = SCXMLDigester.newInstance(null, pathResolver,
+            customActions);
         scxmlDigester.setErrorHandler(errHandler);
 
         try {
@@ -187,7 +298,7 @@
         }
 
         if (scxml != null) {
-            updateSCXML(scxml);
+            ModelUpdater.updateSCXML(scxml);
         }
 
         return scxml;
@@ -209,19 +320,24 @@
      *            The InputSource for the SCXML document
      * @param errHandler
      *            The SAX ErrorHandler
+     * @param customActions
+     *            The list of {@link CustomAction}s this digester
+     *            instance will process, can be null or empty
      *
      * @return SCXML The SCXML object corresponding to the file argument
      *
      * @throws IOException Underlying Digester parsing threw an IOException
      * @throws SAXException Underlying Digester parsing threw a SAXException
+     * @throws ModelException If the resulting document model has flaws
      *
      * @see ErrorHandler
      */
     public static SCXML digest(final InputSource documentInputSource,
-            final ErrorHandler errHandler)
-    throws IOException, SAXException {
+            final ErrorHandler errHandler, final List customActions)
+    throws IOException, SAXException, ModelException {
 
-        Digester scxmlDigester = SCXMLDigester.newInstance(null, null);
+        Digester scxmlDigester = SCXMLDigester.newInstance(null, null,
+            customActions);
         scxmlDigester.setErrorHandler(errHandler);
 
         SCXML scxml = null;
@@ -232,12 +348,12 @@
             // sensible error message about failure in document parsing
             org.apache.commons.logging.Log log = LogFactory.
                 getLog(SCXMLDigester.class);
-            log.error("Could not parse SCXML InputSource", rte);
+            log.error(ERR_ISRC_PARSE_FAIL, rte);
             throw rte;
         }
 
         if (scxml != null) {
-            updateSCXML(scxml);
+            ModelUpdater.updateSCXML(scxml);
         }
 
         return scxml;
@@ -255,8 +371,51 @@
      *       <code>updateSCXML(SCXML)</code> method in this class.</li>
      * </ul>
      *
+     * @return Digester A newly configured SCXML digester instance
+     *
+     * @see SCXMLDigester#updateSCXML(SCXML)
+     */
+    public static Digester newInstance() {
+
+        return newInstance(null, null, null);
+
+    }
+
+    /**
+     * <p>Obtain a SCXML digester instance for further customization.</p>
+     * <b>API Notes:</b>
+     * <ul>
+     *   <li>Use the digest() convenience methods if you do not
+     *       need a custom digester.</li>
+     *   <li>After the SCXML document is parsed by the customized digester,
+     *       the object model <b>must</b> be made executor-ready by calling
+     *       <code>updateSCXML(SCXML)</code> method in this class.</li>
+     * </ul>
+     *
+     * @param pr The PathResolver, may be null for standalone documents
+     * @return Digester A newly configured SCXML digester instance
+     *
+     * @see SCXMLDigester#updateSCXML(SCXML)
+     */
+    public static Digester newInstance(final PathResolver pr) {
+
+        return newInstance(null, pr, null);
+
+    }
+
+    /**
+     * <p>Obtain a SCXML digester instance for further customization.</p>
+     * <b>API Notes:</b>
+     * <ul>
+     *   <li>Use the digest() convenience methods if you do not
+     *       need a custom digester.</li>
+     *   <li>After the SCXML document is parsed by the customized digester,
+     *       the object model <b>must</b> be made executor-ready by calling
+     *       <code>updateSCXML(SCXML)</code> method in this class.</li>
+     * </ul>
+     *
      * @param scxml The parent SCXML document if there is one (in case of
-     *              state templates for examples), null otherwise
+     *              state templates for example), null otherwise
      * @param pr The PathResolver, may be null for standalone documents
      * @return Digester A newly configured SCXML digester instance
      *
@@ -265,85 +424,55 @@
     public static Digester newInstance(final SCXML scxml,
             final PathResolver pr) {
 
+        return newInstance(scxml, pr, null);
+
+    }
+
+    /**
+     * <p>Obtain a SCXML digester instance for further customization.</p>
+     * <b>API Notes:</b>
+     * <ul>
+     *   <li>Use the digest() convenience methods if you do not
+     *       need a custom digester.</li>
+     *   <li>After the SCXML document is parsed by the customized digester,
+     *       the object model <b>must</b> be made executor-ready by calling
+     *       <code>updateSCXML(SCXML)</code> method in this class.</li>
+     * </ul>
+     *
+     * @param scxml The parent SCXML document if there is one (in case of
+     *              state templates for example), null otherwise
+     * @param pr The PathResolver, may be null for standalone documents
+     * @param customActions The list of {@link CustomAction}s this digester
+     *              instance will process, can be null or empty
+     * @return Digester A newly configured SCXML digester instance
+     *
+     * @see SCXMLDigester#updateSCXML(SCXML)
+     */
+    public static Digester newInstance(final SCXML scxml,
+            final PathResolver pr, final List customActions) {
+
         Digester digester = new Digester();
         digester.setNamespaceAware(true);
         //Uncomment next line after SCXML DTD is available
         //digester.setValidating(true);
-        digester.setRuleNamespaceURI(NAMESPACE_SCXML);
-        digester.setRules(initRules(scxml, pr));
+        digester.setRules(initRules(scxml, pr, customActions));
         return digester;
     }
 
-     /**
-      * <p>Update the SCXML object model and make it SCXMLExecutor ready.
-      * This is part of post-digester processing, and sets up the necessary
-      * object references throughtout the SCXML object model for the parsed
-      * document.</p>
-      *
-      * @param scxml The SCXML object (output from Digester)
-      */
-    public static void updateSCXML(final SCXML scxml) {
-        // Watch case, slightly unfortunate naming ;-)
-        String initialstate = scxml.getInitialstate();
-        //we have to use getTargets() here since the initialState can be
-        //an indirect descendant
-        // Concern marked by one of the code reviewers: better type check,
-        //            now ClassCastException happens for Parallel
-        // Response: initial should be a State, for Parallel, it is implicit
-        State initialState = (State) scxml.getTargets().get(initialstate);
-        if (initialState == null) {
-            // Where do we, where do we go?
-            logModelError(ERR_SCXML_NO_INIT, new Object[] {initialstate});
-        }
-        scxml.setInitialState(initialState);
-        Map targets = scxml.getTargets();
-        Map states = scxml.getStates();
-        Iterator i = states.keySet().iterator();
-        while (i.hasNext()) {
-            updateState((State) states.get(i.next()), targets);
-        }
-    }
-
-    /**
-     * <p>Add a custom namespaced action as part of SCXML executable
-     * content.</p>
-     *
-     * @param digester The SCXML digester, <b>must</b> be obtained by
-     *                 calling the
-     *     <code>SCXMLDigester#newInstance(SCXML,PathResolver)</code>
-     *                 method.
-     * @param namespaceURI The namespace URI for this custom action.
-     * @param localName The local name for this custom action.
-     * @param klass The class that will represent this custom action
-     *              in the Commons SCXML object model, <b>must</b> be
-     *              a subtype of
-     *              <code>org.apache.commons.scxml.model.Action</code>
-     */
-    public static void addCustomAction(final Digester digester,
-            final String namespaceURI, final String localName,
-            final Class klass) {
-        if (SCXMLHelper.isStringEmpty(namespaceURI)) {
-            throw new IllegalArgumentException(ERR_CUSTOM_ACTION_NO_NS);
-        }
-        if (namespaceURI.trim().equals(NAMESPACE_SCXML)) {
-            throw new IllegalArgumentException(ERR_CUSTOM_ACTION_RESERVED_NS);
-        }
-        if (SCXMLHelper.isStringEmpty(localName)) {
-            throw new IllegalArgumentException(ERR_CUSTOM_ACTION_NO_NAME);
-        }
-        if (klass == null || !SCXMLHelper.subtypeOf(klass, Action.class)) {
-            throw new IllegalArgumentException(ERR_CUSTOM_ACTION_TYPE);
-        }
-        String xpfLocalName = STR_SLASH + localName.trim();
-        digester.setRuleNamespaceURI(namespaceURI.trim());
-        ExtendedBaseRules scxmlRules = (ExtendedBaseRules) digester.
-            getRules();
-        if (SCXMLHelper.implementationOf(klass, ExternalContent.class)) {
-            addCustomActionRules(xpfLocalName, scxmlRules, klass, true);
-        } else {
-            addCustomActionRules(xpfLocalName, scxmlRules, klass, false);
-        }
-    }
+    /**
+     * <p>Update the SCXML object model and make it SCXMLExecutor ready.
+     * This is part of post-digester processing, and sets up the necessary
+     * object references throughtout the SCXML object model for the parsed
+     * document. Should be used only if a customized digester obtained
+     * using the <code>newInstance()</code> methods is needed.</p>
+     *
+     * @param scxml The SCXML object (output from Digester)
+     * @throws ModelException If the document model has flaws
+     */
+   public static void updateSCXML(final SCXML scxml)
+   throws ModelException {
+       ModelUpdater.updateSCXML(scxml);
+   }
 
     //---------------------- PRIVATE CONSTANTS ----------------------//
     //// Patterns to get the digestion going, prefixed by XP_
@@ -442,77 +571,57 @@
     //// Other constants
     // Error messages
     /**
-     * Parsing SCXML document has failed.
-     * This message may be rendered hence wrapped in a comment.
-     */
-    private static final String ERR_DOC_PARSE_FAIL = "<!-- Error parsing "
-        + "SCXML document: \"{0}\", with message: \"{1}\" -->\n";
-
-    /**
-     * Error message when SCXML document specifies an illegal initial state.
+     * Null URL passed as argument.
      */
-    private static final String ERR_SCXML_NO_INIT = "No SCXML child state "
-        + "with ID \"{0}\" found; illegal initialstate for SCXML document";
+    private static final String ERR_NULL_URL = "Cannot parse null URL";
 
     /**
-     * Error message when a state element specifies an initial state which
-     * cannot be found.
+     * Null path passed as argument.
      */
-    private static final String ERR_STATE_NO_INIT = "No initial element "
-        + "available for \"{0}\"";
+    private static final String ERR_NULL_PATH = "Cannot parse null URL";
 
     /**
-     * Error message when a state element specifies an initial state which
-     * is not a direct descendent.
+     * Null InputSource passed as argument.
      */
-    private static final String ERR_STATE_BAD_INIT = "Initial state "
-        + "null or not a descendant of \"{0}\"";
+    private static final String ERR_NULL_ISRC = "Cannot parse null URL";
 
     /**
-     * Error message when a referenced history state cannot be found.
-     */
-    private static final String ERR_STATE_NO_HIST = "Referenced history state "
-        + "null for \"{0}\"";
-
-    /**
-     * Error message when a shallow history state is not a child state.
+     * Parsing SCXML document has failed.
      */
-    private static final String ERR_STATE_BAD_SHALLOW_HIST = "History state"
-        + " for shallow history is not child for \"{0}\"";
+    private static final String ERR_DOC_PARSE_FAIL = "Error parsing "
+        + "SCXML document: \"{0}\", with message: \"{1}\"\n";
 
     /**
-     * Error message when a deep history state is not a descendent state.
+     * Parsing SCXML document InputSource has failed.
      */
-    private static final String ERR_STATE_BAD_DEEP_HIST = "History state"
-        + " for deep history is not descendant for \"{0}\"";
+    private static final String ERR_ISRC_PARSE_FAIL =
+        "Could not parse SCXML InputSource";
 
     /**
-     * Error message while attempting to define a custom action without
-     * specifying a namespace.
+     * Parser configuration error while registering data rule.
      */
-    private static final String ERR_CUSTOM_ACTION_NO_NS = "Custom action"
-        + " needs a namespace";
+    private static final String ERR_PARSER_CFG_DATA = "XML Parser "
+        + "misconfiguration, error registering <data> element rule";
 
     /**
-     * Error message while attempting to define a custom action in the
-     * SCXML namespace.
+     * Parser configuration error while registering send rule.
      */
-    private static final String ERR_CUSTOM_ACTION_RESERVED_NS = "Cannot add"
-        + " custom action to SCXML namespace";
+    private static final String ERR_PARSER_CFG_SEND = "XML Parser "
+        + "misconfiguration, error registering <send> element rule";
 
     /**
-     * Error message while attempting to define a custom action in the
-     * SCXML namespace.
+     * Parser configuration error while registering body content rule for
+     * custom action.
      */
-    private static final String ERR_CUSTOM_ACTION_NO_NAME = "Cannot add"
-        + " custom action without a local name";
+    private static final String ERR_PARSER_CFG_CUSTOM = "XML Parser "
+        + "misconfiguration, error registering custom action rules";
 
     /**
      * Error message while attempting to define a custom action which does
      * not extend the Commons SCXML Action base class.
      */
-    private static final String ERR_CUSTOM_ACTION_TYPE = "Custom action"
-        + " must be a subtype of org.apache.commons.scxml.model.Action";
+    private static final String ERR_CUSTOM_ACTION_TYPE = "Custom actions list"
+        + " contained unknown object (not a Commons SCXML Action subtype)";
 
     // String constants
     /** Slash. */
@@ -530,11 +639,13 @@
      *
      * @param scxml The parent SCXML document (or null)
      * @param pr The PathResolver
-     * @return ExtendedBaseRules The Digester rules configured for the
-     *                           current document
+     * @param customActions The list of custom actions this digester needs
+     *                      to be able to process
+     *
+     * @return scxmlRules The rule set to be used for digestion
      */
     private static ExtendedBaseRules initRules(final SCXML scxml,
-            final PathResolver pr) {
+            final PathResolver pr, final List customActions) {
 
         ExtendedBaseRules scxmlRules = new ExtendedBaseRules();
 
@@ -547,24 +658,30 @@
 
         //// States
         // Level one states
-        addStateRules(XP_SM_ST, scxmlRules, scxml, pr, 0);
+        addStateRules(XP_SM_ST, scxmlRules, customActions, scxml, pr, 0);
         scxmlRules.add(XP_SM_ST, new SetNextRule("addState"));
         // Nested states
-        addStateRules(XPU_ST_ST, scxmlRules, scxml, pr, 1);
+        addStateRules(XPU_ST_ST, scxmlRules, customActions, scxml, pr, 1);
         scxmlRules.add(XPU_ST_ST, new SetNextRule("addChild"));
 
         // Parallel states
-        addStateRules(XPU_PAR_ST, scxmlRules, scxml, pr, 1);
+        addStateRules(XPU_PAR_ST, scxmlRules, customActions, scxml, pr, 1);
         scxmlRules.add(XPU_PAR_ST, new SetNextRule("addState"));
         // Target states
-        addStateRules(XPU_TR_TAR_ST, scxmlRules, scxml, pr, 2);
+        addStateRules(XPU_TR_TAR_ST, scxmlRules, customActions, scxml, pr, 2);
         scxmlRules.add(XPU_TR_TAR_ST, new SetNextRule("setTarget"));
 
         //// Parallels
-        addParallelRules(XPU_ST_PAR, scxmlRules, scxml);
+        addParallelRules(XPU_ST_PAR, scxmlRules, customActions, scxml);
 
         //// Ifs
-        addIfRules(XPU_IF, scxmlRules);
+        addIfRules(XPU_IF, scxmlRules, customActions);
+
+        //// Custom actions
+        addCustomActionRules(XPU_ONEN, scxmlRules, customActions);
+        addCustomActionRules(XPU_ONEX, scxmlRules, customActions);
+        addCustomActionRules(XPU_TR, scxmlRules, customActions);
+        addCustomActionRules(XPU_IF, scxmlRules, customActions);
 
         return scxmlRules;
 
@@ -576,22 +693,25 @@
      * @param xp The Digester style XPath expression of the parent
      *           XML element
      * @param scxmlRules The rule set to be used for digestion
+     * @param customActions The list of custom actions this digester needs
+     *                      to be able to process
      * @param scxml The parent SCXML document (or null)
      * @param pr The PathResolver
      * @param parent The distance between this state and its parent
      *               state on the Digester stack
      */
     private static void addStateRules(final String xp,
-            final ExtendedBaseRules scxmlRules, final SCXML scxml,
-            final PathResolver pr, final int parent) {
+            final ExtendedBaseRules scxmlRules, final List customActions,
+            final SCXML scxml, final PathResolver pr, final int parent) {
         scxmlRules.add(xp, new ObjectCreateRule(State.class));
-        addStatePropertiesRules(xp, scxmlRules, pr);
+        addStatePropertiesRules(xp, scxmlRules, customActions, pr);
         addDatamodelRules(xp + XPF_DM, scxmlRules, scxml, pr);
-        addInitialRules(xp + XPF_INI, scxmlRules, pr, scxml);
-        addHistoryRules(xp + XPF_HIST, scxmlRules, pr, scxml);
+        addInitialRules(xp + XPF_INI, scxmlRules, customActions, pr, scxml);
+        addHistoryRules(xp + XPF_HIST, scxmlRules, customActions, pr, scxml);
         addParentRule(xp, scxmlRules, parent);
-        addTransitionRules(xp + XPF_TR, scxmlRules, "addTransition");
-        addHandlerRules(xp, scxmlRules);
+        addTransitionRules(xp + XPF_TR, scxmlRules, "addTransition",
+            customActions);
+        addHandlerRules(xp, scxmlRules, customActions);
         scxmlRules.add(xp, new UpdateModelRule(scxml));
     }
 
@@ -601,13 +721,16 @@
      * @param xp The Digester style XPath expression of the parent
      *           XML element
      * @param scxmlRules The rule set to be used for digestion
+     * @param customActions The list of custom actions this digester needs
+     *                      to be able to process
      * @param scxml The parent SCXML document (or null)
      */
     private static void addParallelRules(final String xp,
-            final ExtendedBaseRules scxmlRules, final SCXML scxml) {
+            final ExtendedBaseRules scxmlRules, final List customActions,
+            final SCXML scxml) {
         addSimpleRulesTuple(xp, scxmlRules, Parallel.class, null, null,
                 "setParallel");
-        addHandlerRules(xp, scxmlRules);
+        addHandlerRules(xp, scxmlRules, customActions);
         addParentRule(xp, scxmlRules, 1);
         scxmlRules.add(xp, new UpdateModelRule(scxml));
     }
@@ -618,13 +741,16 @@
      * @param xp The Digester style XPath expression of the parent
      *           XML element
      * @param scxmlRules The rule set to be used for digestion
+     * @param customActions The list of custom actions this digester needs
+     *                      to be able to process
      * @param pr The PathResolver
      */
     private static void addStatePropertiesRules(final String xp,
-            final ExtendedBaseRules scxmlRules, final PathResolver pr) {
+            final ExtendedBaseRules scxmlRules, final List customActions,
+            final PathResolver pr) {
         scxmlRules.add(xp, new SetPropertiesRule(new String[] {"id", "final"},
             new String[] {"id", "isFinal"}));
-        scxmlRules.add(xp, new DigestSrcAttributeRule(pr));
+        scxmlRules.add(xp, new DigestSrcAttributeRule(customActions, pr));
     }
 
     /**
@@ -648,8 +774,7 @@
         } catch (ParserConfigurationException pce) {
             org.apache.commons.logging.Log log = LogFactory.
                 getLog(SCXMLDigester.class);
-            log.error("Error registering rule for parsing <data>"
-                + " element content", pce);
+            log.error(ERR_PARSER_CFG_DATA, pce);
         }
         scxmlRules.add(xp, new SetNextRule("setDatamodel"));
     }
@@ -660,16 +785,19 @@
      * @param xp The Digester style XPath expression of the parent
      *           XML element
      * @param scxmlRules The rule set to be used for digestion
+     * @param customActions The list of custom actions this digester needs
+     *                      to be able to process
      * @param pr The PathResolver
      * @param scxml The parent SCXML document (or null)
      */
     private static void addInitialRules(final String xp,
-            final ExtendedBaseRules scxmlRules, final PathResolver pr,
-            final SCXML scxml) {
+            final ExtendedBaseRules scxmlRules, final List customActions,
+            final PathResolver pr, final SCXML scxml) {
         scxmlRules.add(xp, new ObjectCreateRule(Initial.class));
-        addPseudoStatePropertiesRules(xp, scxmlRules, pr);
+        addPseudoStatePropertiesRules(xp, scxmlRules, customActions, pr);
         scxmlRules.add(xp, new UpdateModelRule(scxml));
-        addTransitionRules(xp + XPF_TR, scxmlRules, "setTransition");
+        addTransitionRules(xp + XPF_TR, scxmlRules, "setTransition",
+            customActions);
         scxmlRules.add(xp, new SetNextRule("setInitial"));
     }
 
@@ -679,18 +807,21 @@
      * @param xp The Digester style XPath expression of the parent
      *           XML element
      * @param scxmlRules The rule set to be used for digestion
+     * @param customActions The list of custom actions this digester needs
+     *                      to be able to process
      * @param pr The PathResolver
      * @param scxml The parent SCXML document (or null)
      */
     private static void addHistoryRules(final String xp,
-            final ExtendedBaseRules scxmlRules, final PathResolver pr,
-            final SCXML scxml) {
+            final ExtendedBaseRules scxmlRules, final List customActions,
+            final PathResolver pr, final SCXML scxml) {
         scxmlRules.add(xp, new ObjectCreateRule(History.class));
-        addPseudoStatePropertiesRules(xp, scxmlRules, pr);
+        addPseudoStatePropertiesRules(xp, scxmlRules, customActions, pr);
         scxmlRules.add(xp, new UpdateModelRule(scxml));
         scxmlRules.add(xp, new SetPropertiesRule(new String[] {"type"},
             new String[] {"type"}));
-        addTransitionRules(xp + XPF_TR, scxmlRules, "setTransition");
+        addTransitionRules(xp + XPF_TR, scxmlRules, "setTransition",
+            customActions);
         scxmlRules.add(xp, new SetNextRule("addHistory"));
     }
 
@@ -701,13 +832,16 @@
      * @param xp The Digester style XPath expression of the parent
      *           XML element
      * @param scxmlRules The rule set to be used for digestion
+     * @param customActions The list of custom actions this digester needs
+     *                      to be able to process
      * @param pr The PathResolver
      */
     private static void addPseudoStatePropertiesRules(final String xp,
-            final ExtendedBaseRules scxmlRules, final PathResolver pr) {
+            final ExtendedBaseRules scxmlRules, final List customActions,
+            final PathResolver pr) {
         scxmlRules.add(xp, new SetPropertiesRule(new String[] {"id"},
             new String[] {"id"}));
-        scxmlRules.add(xp, new DigestSrcAttributeRule(pr));
+        scxmlRules.add(xp, new DigestSrcAttributeRule(customActions, pr));
         addParentRule(xp, scxmlRules, 1);
     }
 
@@ -746,15 +880,18 @@
      * @param scxmlRules The rule set to be used for digestion
      * @param setNextMethod The method name for adding this transition
      *             to its parent (defined by the SCXML Java object model).
+     * @param customActions The list of custom actions this digester needs
+     *                      to be able to process
      */
     private static void addTransitionRules(final String xp,
-            final ExtendedBaseRules scxmlRules, final String setNextMethod) {
+            final ExtendedBaseRules scxmlRules, final String setNextMethod,
+            final List customActions) {
         scxmlRules.add(xp, new ObjectCreateRule(Transition.class));
         scxmlRules.add(xp, new SetPropertiesRule(
             new String[] {"event", "cond", "target"},
             new String[] {"event", "cond", "next"}));
         scxmlRules.add(xp + XPF_TAR, new SetPropertiesRule());
-        addActionRules(xp, scxmlRules);
+        addActionRules(xp, scxmlRules, customActions);
         scxmlRules.add(xp + XPF_EXT, new Rule() {
             public void end(final String namespace, final String name) {
                 Transition t = (Transition) getDigester().peek(1);
@@ -773,14 +910,16 @@
      * @param xp The Digester style XPath expression of the parent
      *           XML element
      * @param scxmlRules The rule set to be used for digestion
+     * @param customActions The list of custom actions this digester needs
+     *                      to be able to process
      */
     private static void addHandlerRules(final String xp,
-            final ExtendedBaseRules scxmlRules) {
+            final ExtendedBaseRules scxmlRules, final List customActions) {
         scxmlRules.add(xp + XPF_ONEN, new ObjectCreateRule(OnEntry.class));
-        addActionRules(xp + XPF_ONEN, scxmlRules);
+        addActionRules(xp + XPF_ONEN, scxmlRules, customActions);
         scxmlRules.add(xp + XPF_ONEN, new SetNextRule("setOnEntry"));
         scxmlRules.add(xp + XPF_ONEX, new ObjectCreateRule(OnExit.class));
-        addActionRules(xp + XPF_ONEX, scxmlRules);
+        addActionRules(xp + XPF_ONEX, scxmlRules, customActions);
         scxmlRules.add(xp + XPF_ONEX, new SetNextRule("setOnExit"));
     }
 
@@ -790,15 +929,56 @@
      * @param xp The Digester style XPath expression of the parent
      *           XML element
      * @param scxmlRules The rule set to be used for digestion
+     * @param customActions The list of custom actions this digester needs
+     *                      to be able to process
      */
     private static void addActionRules(final String xp,
-            final ExtendedBaseRules scxmlRules) {
+            final ExtendedBaseRules scxmlRules, final List customActions) {
         addActionRulesTuple(xp + XPF_ASN, scxmlRules, Assign.class);
         addActionRulesTuple(xp + XPF_VAR, scxmlRules, Var.class);
         addActionRulesTuple(xp + XPF_LOG, scxmlRules, Log.class);
         addSendRulesTuple(xp + XPF_SND, scxmlRules);
         addActionRulesTuple(xp + XPF_CAN, scxmlRules, Cancel.class);
         addActionRulesTuple(xp + XPF_EXT, scxmlRules, Exit.class);
+        //addCustomActionRules(xp, scxmlRules, customActions);
+    }
+
+    /**
+     * Add custom action rules, if any custom actions are provided.
+     *
+     * @param xp The Digester style XPath expression of the parent
+     *           XML element
+     * @param scxmlRules The rule set to be used for digestion
+     * @param customActions The list of custom actions this digester needs
+     *                      to be able to process
+     */
+    private static void addCustomActionRules(final String xp,
+            final ExtendedBaseRules scxmlRules, final List customActions) {
+        if (customActions == null || customActions.size() == 0) {
+            return;
+        }
+        for (int i = 0; i < customActions.size(); i++) {
+            Object item = customActions.get(i);
+            if (item == null || !(item instanceof CustomAction)) {
+                org.apache.commons.logging.Log log = LogFactory.
+                    getLog(SCXMLDigester.class);
+                log.warn(ERR_CUSTOM_ACTION_TYPE);
+            } else {
+                CustomAction ca = (CustomAction) item;
+                scxmlRules.setNamespaceURI(ca.getNamespaceURI());
+                String xpfLocalName = STR_SLASH + ca.getLocalName();
+                Class klass = ca.getActionClass();
+                if (SCXMLHelper.implementationOf(klass,
+                        ExternalContent.class)) {
+                    addCustomActionRulesTuple(xp + xpfLocalName, scxmlRules,
+                        klass, true);
+                } else {
+                    addCustomActionRulesTuple(xp + xpfLocalName, scxmlRules,
+                        klass, false);
+                }
+            }
+        }
+        scxmlRules.setNamespaceURI(NAMESPACE_SCXML);
     }
 
     /**
@@ -816,38 +996,11 @@
         } catch (ParserConfigurationException pce) {
             org.apache.commons.logging.Log log = LogFactory.
                 getLog(SCXMLDigester.class);
-            log.error("Error registering rule for parsing <send>"
-                + " element content", pce);
+            log.error(ERR_PARSER_CFG_SEND, pce);
         }
     }
 
     /**
-     * Add Digester rules for a custom action with child nodes (in
-     * external namespaces).
-     *
-     * @param xpfLocalName The local name path fragment (trailing) for
-     *                       the custom action element
-     * @param scxmlRules The rule set to be used for digestion
-     * @param klass The <code>Action</code> class implementing the custom
-     *              action.
-     * @param bodyContent Whether the custom rule has body content
-     *              that should be parsed using
-     *              <code>NodeCreateRule</code>
-     */
-    private static void addCustomActionRules(final String xpfLocalName,
-            final ExtendedBaseRules scxmlRules, final Class klass,
-            final boolean bodyContent) {
-        addCustomActionRulesTuple(XPU_ONEN + xpfLocalName, scxmlRules,
-            klass, bodyContent);
-        addCustomActionRulesTuple(XPU_ONEX + xpfLocalName, scxmlRules,
-            klass, bodyContent);
-        addCustomActionRulesTuple(XPU_TR + xpfLocalName, scxmlRules,
-            klass, bodyContent);
-        addCustomActionRulesTuple(XPU_IF + xpfLocalName, scxmlRules,
-            klass, bodyContent);
-    }
-
-    /**
      * Add Digester rules for a simple custom action (no body content).
      *
      * @param xp The path to the custom action element
@@ -868,8 +1021,7 @@
             } catch (ParserConfigurationException pce) {
                 org.apache.commons.logging.Log log = LogFactory.
                     getLog(SCXMLDigester.class);
-                log.error("Error instantiating body content parsing rule for"
-                    + " custom action", pce);
+                log.error(ERR_PARSER_CFG_CUSTOM, pce);
             }
         }
     }
@@ -880,11 +1032,13 @@
      * @param xp The Digester style XPath expression of the parent
      *           XML element
      * @param scxmlRules The rule set to be used for digestion
+     * @param customActions The list of custom actions this digester needs
+     *                      to be able to process
      */
     private static void addIfRules(final String xp,
-            final ExtendedBaseRules scxmlRules) {
+            final ExtendedBaseRules scxmlRules, final List customActions) {
         addActionRulesTuple(xp, scxmlRules, If.class);
-        addActionRules(xp, scxmlRules);
+        addActionRules(xp, scxmlRules, customActions);
         addActionRulesTuple(xp + XPF_EIF, scxmlRules, ElseIf.class);
         addActionRulesTuple(xp + XPF_ELS, scxmlRules, Else.class);
     }
@@ -929,152 +1083,6 @@
         scxmlRules.add(xp, new SetNextRule(addMethod));
     }
 
-    /*
-     * Post-processing methods to make the SCXML object SCXMLExecutor ready.
-     */
-    /**
-      * Update this State object (part of post-digestion processing).
-      * Also checks for any errors in the document.
-      *
-      * @param s The State object
-      * @param targets The global Map of all transition targets
-      */
-    private static void updateState(final State s, final Map targets) {
-        //ensure both onEntry and onExit have parent
-        //could add next two lines as a Digester rule for OnEntry/OnExit
-        s.getOnEntry().setParent(s);
-        s.getOnExit().setParent(s);
-        //initialize next / inital
-        Initial ini = s.getInitial();
-        Map c = s.getChildren();
-        if (!c.isEmpty()) {
-            if (ini == null) {
-                logModelError(ERR_STATE_NO_INIT,
-                    new Object[] {getStateName(s)});
-            }
-            Transition initialTransition = ini.getTransition();
-            updateTransition(initialTransition, targets);
-            TransitionTarget initialState = initialTransition.getTarget();
-            // we have to allow for an indirect descendant initial (targets)
-            //check that initialState is a descendant of s
-            if (initialState == null
-                    || !SCXMLHelper.isDescendant(initialState, s)) {
-                logModelError(ERR_STATE_BAD_INIT,
-                    new Object[] {getStateName(s)});
-            }
-        }
-        List histories = s.getHistory();
-        Iterator histIter = histories.iterator();
-        while (histIter.hasNext()) {
-            History h = (History) histIter.next();
-            Transition historyTransition = h.getTransition();
-            updateTransition(historyTransition, targets);
-            State historyState = (State) historyTransition.getTarget();
-            if (historyState == null) {
-                logModelError(ERR_STATE_NO_HIST,
-                    new Object[] {getStateName(s)});
-            }
-            if (!h.isDeep()) {
-                if (!c.containsValue(historyState)) {
-                    logModelError(ERR_STATE_BAD_SHALLOW_HIST, new Object[] {
-                        getStateName(s) });
-                }
-            } else {
-                if (!SCXMLHelper.isDescendant(historyState, s)) {
-                    logModelError(ERR_STATE_BAD_DEEP_HIST, new Object[] {
-                        getStateName(s) });
-                }
-            }
-        }
-        Map t = s.getTransitions();
-        Iterator i = t.keySet().iterator();
-        while (i.hasNext()) {
-            Iterator j = ((List) t.get(i.next())).iterator();
-            while (j.hasNext()) {
-                Transition trn = (Transition) j.next();
-                //could add next two lines as a Digester rule for Transition
-                trn.setParent(s);
-                updateTransition(trn, targets);
-            }
-        }
-        Parallel p = s.getParallel();
-        if (p != null) {
-            updateParallel(p, targets);
-        } else {
-            Iterator j = c.keySet().iterator();
-            while (j.hasNext()) {
-                updateState((State) c.get(j.next()), targets);
-            }
-        }
-    }
-
-    /**
-      * Update this Parallel object (part of post-digestion processing).
-      *
-      * @param p The Parallel object
-      * @param targets The global Map of all transition targets
-      */
-    private static void updateParallel(final Parallel p, final Map targets) {
-        Iterator i = p.getStates().iterator();
-        while (i.hasNext()) {
-            updateState((State) i.next(), targets);
-        }
-    }
-
-    /**
-      * Update this Transition object (part of post-digestion processing).
-      *
-      * @param t The Transition object
-      * @param targets The global Map of all transition targets
-      */
-    private static void updateTransition(final Transition t,
-            final Map targets) {
-        String next = t.getNext();
-        TransitionTarget tt = t.getTarget();
-        if (tt == null) {
-            tt = (TransitionTarget) targets.get(next);
-            if (tt == null) {
-                // Could move Digester warnings to errors
-                org.apache.commons.logging.Log log = LogFactory.
-                    getLog(SCXMLDigester.class);
-                log.warn("WARNING: SCXMLDigester - Transition "
-                        + "target \"" + next + "\" not found");
-            }
-            t.setTarget(tt);
-        }
-    }
-
-    /**
-      * Log an error discovered in post-digestion processing.
-      *
-      * @param errType The type of error
-      * @param msgArgs The arguments for formatting the error message
-      */
-    private static void logModelError(final String errType,
-            final Object[] msgArgs) {
-        MessageFormat msgFormat = new MessageFormat(errType);
-        String errMsg = msgFormat.format(msgArgs);
-        org.apache.commons.logging.Log log = LogFactory.
-            getLog(SCXMLDigester.class);
-        log.error(errMsg);
-    }
-
-     /**
-      * Get state identifier for error message. This method is only
-      * called to produce an appropriate log message in some error
-      * conditions.
-      *
-      * @param state The <code>State</code> object
-      * @return The state identifier for the error message
-      */
-    private static String getStateName(final State state) {
-        String badState = "anonymous state";
-        if (!SCXMLHelper.isStringEmpty(state.getId())) {
-            badState = "state with ID " + state.getId();
-        }
-        return badState;
-    }
-
     /**
      * Discourage instantiation since this is a utility class.
      */
@@ -1278,12 +1286,25 @@
         private PathResolver pr;
 
         /**
+         * The list of custom actions the parent document is capable of
+         * processing (and hence, the child should be, by transitivity).
+         * @see CustomAction
+         */
+        private List customActions;
+
+        /**
          * Constructor.
          * @param pr The PathResolver
+         * @param customActions The list of custom actions this digester needs
+         *                      to be able to process
+         *
          * @see PathResolver
+         * @see CustomAction
          */
-        public DigestSrcAttributeRule(final PathResolver pr) {
+        public DigestSrcAttributeRule(final List customActions,
+                final PathResolver pr) {
             super();
+            this.customActions = customActions;
             this.pr = pr;
         }
 
@@ -1304,10 +1325,11 @@
             Digester externalSrcDigester;
             if (pr == null) {
                 path = src;
-                externalSrcDigester = newInstance(scxml, null);
+                externalSrcDigester = newInstance(scxml, null, customActions);
             } else {
                 path = pr.resolvePath(src);
-                externalSrcDigester = newInstance(scxml, pr.getResolver(src));
+                externalSrcDigester = newInstance(scxml, pr.getResolver(src),
+                    customActions);
             }
 
             try {

Added: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/model/CustomAction.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/model/CustomAction.java?rev=407424&view=auto
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/model/CustomAction.java (added)
+++ jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/model/CustomAction.java Wed May 17 18:16:30 2006
@@ -0,0 +1,149 @@
+/*
+ *
+ *   Copyright 2006 The Apache Software Foundation.
+ *
+ *  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.apache.commons.scxml.model;
+
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.scxml.SCXMLHelper;
+
+/**
+ * A custom action is simply a tuple consisting of a namespace URI,
+ * the local name for the custom action and the corresponding
+ * {@link Action} class.
+ *
+ */
+public class CustomAction {
+
+    /**
+     * Error logged while attempting to define a custom action
+     * in a null or empty namespace.
+     */
+    private static final String ERR_NO_NAMESPACE =
+        "Cannot define a custom SCXML action with a null or empty namespace";
+
+    /**
+     * The SCXML namespace, to which custom actions may not be added.
+     */
+    private static final String NAMESPACE_SCXML =
+        "http://www.w3.org/2005/07/scxml";
+
+    /**
+     * Error logged while attempting to define a custom action
+     * with the SCXML namespace.
+     */
+    private static final String ERR_RESERVED_NAMESPACE =
+        "Cannot define a custom SCXML action within the SCXML namespace '"
+        + NAMESPACE_SCXML + "'";
+
+    /**
+     * Error logged while attempting to define a custom action
+     * in a null or empty local name.
+     */
+    private static final String ERR_NO_LOCAL_NAME =
+        "Cannot define a custom SCXML action with a null or empty local name";
+
+    /**
+     * Error logged while attempting to define a custom action
+     * which does not extend {@link Action}.
+     */
+    private static final String ERR_NOT_AN_ACTION =
+        "Custom SCXML action does not extend Action superclass";
+
+    /**
+     * The namespace this custom action belongs to.
+     */
+    private String namespaceURI;
+
+    /**
+     * The local name of the custom action.
+     */
+    private String localName;
+
+    /**
+     * The implementation of this custom action.
+     */
+    private Class actionClass;
+
+    /**
+     * The log for this custom action.
+     */
+    private org.apache.commons.logging.Log log;
+
+    /**
+     * Constructor, if the namespace or local name is null or empty,
+     * or if the implementation is not an {@link Action}, an
+     * {@link IllegalArgumentException} is thrown.
+     *
+     * @param namespaceURI The namespace URI for this custom action.
+     * @param localName The local name for this custom action.
+     * @param actionClass The {@link Action} subclass implementing this
+     *                    custom action.
+     */
+    public CustomAction(final String namespaceURI, final String localName,
+            final Class actionClass) {
+        this.log = LogFactory.getLog(CustomAction.class);
+        if (SCXMLHelper.isStringEmpty(namespaceURI)) {
+            log.error(ERR_NO_NAMESPACE);
+            throw new IllegalArgumentException(ERR_NO_NAMESPACE);
+        }
+        if (namespaceURI.trim().equalsIgnoreCase(NAMESPACE_SCXML)) {
+            log.error(ERR_RESERVED_NAMESPACE);
+            throw new IllegalArgumentException(ERR_RESERVED_NAMESPACE);
+        }
+        if (SCXMLHelper.isStringEmpty(localName)) {
+            log.error(ERR_NO_LOCAL_NAME);
+            throw new IllegalArgumentException(ERR_NO_LOCAL_NAME);
+        }
+        if (actionClass == null
+                || !SCXMLHelper.subtypeOf(actionClass, Action.class)) {
+            log.error(ERR_NOT_AN_ACTION);
+            throw new IllegalArgumentException(ERR_NOT_AN_ACTION);
+        }
+        this.namespaceURI = namespaceURI;
+        this.localName = localName;
+        this.actionClass = actionClass;
+    }
+
+    /**
+     * Get this custom action's implementation.
+     *
+     * @return Returns the action class.
+     */
+    public Class getActionClass() {
+        return actionClass;
+    }
+
+    /**
+     * Get the local name for this custom action.
+     *
+     * @return Returns the local name.
+     */
+    public String getLocalName() {
+        return localName;
+    }
+
+    /**
+     * Get the namespace URI for this custom action.
+     *
+     * @return Returns the namespace URI.
+     */
+    public String getNamespaceURI() {
+        return namespaceURI;
+    }
+
+}
+

Propchange: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/model/CustomAction.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml/model/CustomAction.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/SCXMLTestHelper.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/SCXMLTestHelper.java?rev=407424&r1=407423&r2=407424&view=diff
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/SCXMLTestHelper.java (original)
+++ jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/SCXMLTestHelper.java Wed May 17 18:16:30 2006
@@ -16,10 +16,14 @@
 package org.apache.commons.scxml;
 
 import java.net.URL;
+import java.util.List;
 import java.util.Set;
 
 import junit.framework.Assert;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
 import org.apache.commons.scxml.env.SimpleDispatcher;
 import org.apache.commons.scxml.env.Tracer;
 import org.apache.commons.scxml.env.jexl.JexlContext;
@@ -35,15 +39,24 @@
 public class SCXMLTestHelper {
 
     public static SCXML digest(final URL url) {
-        return digest(url, null);
+        return digest(url, null, null);
+    }
+
+    public static SCXML digest(final URL url, final List customActions) {
+        return digest(url, null, customActions);
     }
 
     public static SCXML digest(final URL url, final ErrorHandler errHandler) {
+        return digest(url, errHandler, null);
+    }
+
+    public static SCXML digest(final URL url, final ErrorHandler errHandler,
+            final List customActions) {
         Assert.assertNotNull(url);
         // SAX ErrorHandler may be null
         SCXML scxml = null;
         try {
-            scxml = SCXMLDigester.digest(url, errHandler);
+            scxml = SCXMLDigester.digest(url, errHandler, customActions);
         } catch (Exception e) {
             Assert.fail(e.getMessage());
         }
@@ -52,14 +65,14 @@
     }
 
     public static SCXMLExecutor getExecutor(final URL url) {
-        SCXML scxml = digest(url, null);
+        SCXML scxml = digest(url);
         Evaluator evaluator = new JexlEvaluator();
         return getExecutor(evaluator, scxml);
     }
 
     public static SCXMLExecutor getExecutor(final URL url,
             final Evaluator evaluator) {
-        SCXML scxml = digest(url, null);
+        SCXML scxml = digest(url);
         return getExecutor(evaluator, scxml);
     }
 
@@ -92,7 +105,7 @@
 
     public static SCXMLExecutor getExecutor(final URL url, final Context ctx,
             final Evaluator evaluator) {
-        SCXML scxml = digest(url, null);
+        SCXML scxml = digest(url);
         EventDispatcher ed = new SimpleDispatcher();
         Tracer trc = new Tracer();
         return getExecutor(ctx, evaluator, scxml, ed, trc);
@@ -131,6 +144,8 @@
             exec.setStateMachine(scxml);
             exec.go();
         } catch (Exception e) {
+            Log log = LogFactory.getLog(SCXMLTestHelper.class);
+            log.error(e.getMessage(), e);
             Assert.fail(e.getMessage());
         }
         Assert.assertNotNull(exec);

Copied: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world-01.xml (from r407046, jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world.xml)
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world-01.xml?p2=jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world-01.xml&p1=jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world.xml&r1=407046&r2=407424&rev=407424&view=diff
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world.xml (original)
+++ jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world-01.xml Wed May 17 18:16:30 2006
@@ -16,15 +16,19 @@
 -->
 <!-- Used for comparison with hello-world.xml by
      CustomActionTest.java in model package -->
-<scxml xmlns="http://www.w3.org/2005/07/SCXML"
-       xmlns:my="http://my.custom-actions.domain/CUSTOM"
+<scxml xmlns:my="http://my.custom-actions.domain/CUSTOM1"
+       xmlns:foo="http://my.custom-actions.domain/CUSTOM2"
        version="1.0"
        initialstate="custom">
 
     <state id="custom" final="true">
+
         <onentry>
             <my:hello name="world" />
+            <!-- foo:bar also maps to Hello action -->
+            <foo:bar name="custom action" />
         </onentry>
+
     </state>
 
 </scxml>

Added: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world-02.xml
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world-02.xml?rev=407424&view=auto
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world-02.xml (added)
+++ jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world-02.xml Wed May 17 18:16:30 2006
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<!--
+   Copyright 2006 The Apache Software Foundation
+
+   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.
+-->
+<!-- Used for CustomActionTest.java in model package -->
+<scxml xmlns="http://www.w3.org/2005/07/SCXML"
+       xmlns:my="http://my.custom-actions.domain/CUSTOM"
+       version="1.0"
+       initialstate="custom">
+
+    <state id="custom" final="true">
+        
+        <onentry>
+            <my:hello name="child (included) document" />
+        </onentry>
+
+    </state>
+
+</scxml>
+

Propchange: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world-02.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/custom-hello-world-02.xml
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/external-hello-world.xml
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/external-hello-world.xml?rev=407424&view=auto
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/external-hello-world.xml (added)
+++ jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/external-hello-world.xml Wed May 17 18:16:30 2006
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<!--
+   Copyright 2006 The Apache Software Foundation
+
+   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.
+-->
+<!-- Used for testing custom actions in external document pulled in via
+     the src attributes by ExternalCustomActionTest.java in model package -->
+<scxml xmlns="http://www.w3.org/2005/07/SCXML"
+       xmlns:my="http://my.custom-actions.domain/CUSTOM"
+       version="1.0"
+       initialstate="external-hello">
+
+    <state id="external-hello" final="true" src="custom-hello-world-02.xml">
+
+        <onentry>
+            <my:hello name="parent document" />
+        </onentry>
+
+    </state>
+
+</scxml>

Propchange: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/external-hello-world.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/external-hello-world.xml
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-deep-01.xml
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-deep-01.xml?rev=407424&view=auto
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-deep-01.xml (added)
+++ jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-deep-01.xml Wed May 17 18:16:30 2006
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<!--
+   Copyright 2006 The Apache Software Foundation
+
+   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.
+-->
+<!--
+   This document is an example of using deep history
+-->
+<scxml xmlns="http://www.w3.org/2005/07/SCXML" version="1.0"
+       initialstate="flow">
+
+    <state id="flow">
+        <initial>
+            <transition target="phases"/>
+        </initial>
+
+        <!-- deep history is specified by setting the type attribute
+             to "deep" -->
+
+        <history id="hist" type="deep">
+
+            <!-- This is the transition to be followed if no
+                 prior history is available -->
+
+            <transition target="phases"/>
+
+        </history>
+
+        <state id="phases">
+
+            <initial>
+                <transition target="phase1"/>
+            </initial>
+
+            <state id="phase1">
+                <transition event="phase.done" target="phase2"/>
+            </state>
+
+            <state id="phase2">
+                <transition event="phase.done" target="phase3"/>
+            </state>
+
+            <state id="phase3" final="true" />
+
+        </state>
+
+        <transition event="flow.pause" target="interrupted"/>
+
+        <transition event="flow.terminate" target="terminated"/>
+
+    </state>
+
+    <state id="interrupted">
+
+        <transition event="flow.resume" target="hist"/>
+
+        <transition event="flow.terminate" target="terminated"/>
+
+    </state>
+
+    <state id="terminated" final="true"/>
+
+</scxml>

Propchange: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-deep-01.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-deep-01.xml
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-default-01.xml
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-default-01.xml?rev=407424&view=auto
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-default-01.xml (added)
+++ jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-default-01.xml Wed May 17 18:16:30 2006
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+<!--
+   Copyright 2006 The Apache Software Foundation
+
+   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.
+-->
+<!--
+   This document is an example of specifying default transitions for
+   history states (if the parent state has never been visited before)
+-->
+<scxml xmlns="http://www.w3.org/2005/07/SCXML" version="1.0"
+       initialstate="state1">
+
+    <state id="state1">
+        <initial>
+            <transition target="history1"/>
+        </initial>
+
+        <!-- shallow history, explicit default transition -->
+
+        <history id="history1">
+            <transition next="state11"/>
+        </history>
+
+        <state id="state11">
+            <transition event="state.next" target="state2"/>
+        </state>
+
+    </state>
+
+    <state id="state2">
+        <initial>
+            <transition target="history2"/>
+        </initial>
+
+        <!-- deep history, explicit default transition -->
+
+        <history id="history2" type="deep">
+            <transition next="state211"/>
+        </history>
+
+        <state id="state21">
+
+            <initial>
+                <transition target="state212"/>
+            </initial>
+
+            <state id="state211">
+                <transition event="state.next" target="history3"/>
+            </state>
+
+            <state id="state212">
+                <transition event="state.next" target="history3"/>
+            </state>
+
+        </state>
+
+    </state>
+
+    <state id="state3">
+
+        <initial>
+            <transition target="state31"/>
+        </initial>
+
+        <!-- shallow history, no default transition specified,
+             reuse initial as default transition -->
+
+        <history id="history3"/>
+
+        <state id="state31">
+                <transition event="state.next" target="state4"/>
+        </state>
+        
+    </state>
+
+    <state id="state4" final="true"/>
+
+</scxml>

Propchange: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-default-01.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-default-01.xml
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-shallow-01.xml
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-shallow-01.xml?rev=407424&view=auto
==============================================================================
--- jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-shallow-01.xml (added)
+++ jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-shallow-01.xml Wed May 17 18:16:30 2006
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<!--
+   Copyright 2006 The Apache Software Foundation
+
+   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.
+-->
+<!--
+   This document is an example of using shallow history
+-->
+<scxml xmlns="http://www.w3.org/2005/07/SCXML" version="1.0"
+    initialstate="flow">
+
+    <state id="flow">
+        <initial>
+            <transition target="phase1"/>
+        </initial>
+
+        <!-- history defaults to shallow, optionally one can set
+             the type attribute to "shallow" for identical results -->
+
+        <history id="hist">
+
+            <!-- This is the transition to be followed if no
+                 prior history is available -->
+
+            <transition target="phase1"/>
+
+        </history>
+
+        <state id="phase1">
+            <transition event="phase.done" target="phase2"/>
+        </state>
+
+        <state id="phase2">
+            <transition event="phase.done" target="phase3"/>
+        </state>
+
+        <state id="phase3" final="true"/>
+
+        <transition event="flow.pause" target="interrupted"/>
+
+        <transition event="flow.terminate" target="terminated"/>
+
+    </state>
+
+    <state id="interrupted">
+
+        <transition event="flow.resume" target="hist"/>
+
+        <transition event="flow.terminate" target="terminated"/>
+
+    </state>
+
+    <state id="terminated" final="true"/>
+
+</scxml>

Propchange: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-shallow-01.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml/history-shallow-01.xml
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org