You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2023/11/03 15:49:48 UTC

(commons-scxml) 02/03: Sort main members

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-scxml.git

commit da2159fffd8489d2fb25e925aa5893639f86b23c
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Fri Nov 3 11:49:33 2023 -0400

    Sort main members
---
 .../commons/scxml2/ActionExecutionContext.java     |   38 +-
 .../java/org/apache/commons/scxml2/Context.java    |   62 +-
 .../java/org/apache/commons/scxml2/Evaluator.java  |   46 +-
 .../apache/commons/scxml2/EvaluatorFactory.java    |   56 +-
 .../apache/commons/scxml2/EvaluatorProvider.java   |   10 +-
 .../org/apache/commons/scxml2/EventBuilder.java    |   50 +-
 .../org/apache/commons/scxml2/EventDispatcher.java |   14 +-
 .../commons/scxml2/NotificationRegistry.java       |   36 +-
 .../commons/scxml2/ParentSCXMLIOProcessor.java     |    8 +-
 .../org/apache/commons/scxml2/PathResolver.java    |   12 +-
 .../java/org/apache/commons/scxml2/SCInstance.java |  452 +-
 .../scxml2/SCInstanceObjectInputStream.java        |   16 +-
 .../commons/scxml2/SCXMLExecutionContext.java      |  426 +-
 .../org/apache/commons/scxml2/SCXMLExecutor.java   |  482 +--
 .../commons/scxml2/SCXMLExpressionException.java   |   16 +-
 .../org/apache/commons/scxml2/SCXMLSemantics.java  |  110 +-
 .../apache/commons/scxml2/SCXMLSystemContext.java  |   96 +-
 .../apache/commons/scxml2/StateConfiguration.java  |   38 +-
 .../java/org/apache/commons/scxml2/Status.java     |   18 +-
 .../org/apache/commons/scxml2/TriggerEvent.java    |  114 +-
 .../commons/scxml2/env/AbstractBaseEvaluator.java  |   38 +-
 .../commons/scxml2/env/AbstractStateMachine.java   |  208 +-
 .../commons/scxml2/env/EffectiveContextMap.java    |   34 +-
 .../org/apache/commons/scxml2/env/LogUtils.java    |   34 +-
 .../apache/commons/scxml2/env/SimpleContext.java   |  108 +-
 .../commons/scxml2/env/SimpleDispatcher.java       |   54 +-
 .../commons/scxml2/env/SimpleErrorReporter.java    |   44 +-
 .../java/org/apache/commons/scxml2/env/Tracer.java |   32 +-
 .../org/apache/commons/scxml2/env/URLResolver.java |   14 +-
 .../commons/scxml2/env/groovy/GroovyContext.java   |   92 +-
 .../scxml2/env/groovy/GroovyContextBinding.java    |   30 +-
 .../commons/scxml2/env/groovy/GroovyEvaluator.java |  116 +-
 .../env/groovy/GroovyExtendableScriptCache.java    |  268 +-
 .../scxml2/env/groovy/GroovySCXMLScript.java       |   60 +-
 .../commons/scxml2/env/javascript/JSBindings.java  |   66 +-
 .../commons/scxml2/env/javascript/JSContext.java   |   16 +-
 .../commons/scxml2/env/javascript/JSEvaluator.java |  242 +-
 .../commons/scxml2/env/jexl/JexlContext.java       |   18 +-
 .../commons/scxml2/env/jexl/JexlEvaluator.java     |   96 +-
 .../commons/scxml2/env/minimal/MinimalContext.java |   14 +-
 .../scxml2/env/minimal/MinimalEvaluator.java       |   44 +-
 .../org/apache/commons/scxml2/invoke/Invoker.java  |   44 +-
 .../commons/scxml2/invoke/InvokerException.java    |   16 +-
 .../commons/scxml2/invoke/SimpleSCXMLInvoker.java  |   68 +-
 .../apache/commons/scxml2/io/ContentParser.java    |  186 +-
 .../org/apache/commons/scxml2/io/ModelUpdater.java |  318 +-
 .../org/apache/commons/scxml2/io/SCXMLReader.java  | 4314 ++++++++++----------
 .../org/apache/commons/scxml2/io/SCXMLWriter.java  | 1452 +++----
 .../org/apache/commons/scxml2/model/Action.java    |   38 +-
 .../commons/scxml2/model/ActionExecutionError.java |    8 +-
 .../commons/scxml2/model/ActionsContainer.java     |   14 +-
 .../org/apache/commons/scxml2/model/Assign.java    |   92 +-
 .../org/apache/commons/scxml2/model/Cancel.java    |   72 +-
 .../org/apache/commons/scxml2/model/Content.java   |   18 +-
 .../commons/scxml2/model/CustomActionWrapper.java  |   78 +-
 .../java/org/apache/commons/scxml2/model/Data.java |   54 +-
 .../org/apache/commons/scxml2/model/Datamodel.java |   18 +-
 .../org/apache/commons/scxml2/model/DoneData.java  |   16 +-
 .../org/apache/commons/scxml2/model/ElseIf.java    |   16 +-
 .../commons/scxml2/model/EnterableState.java       |   52 +-
 .../apache/commons/scxml2/model/Executable.java    |   18 +-
 .../org/apache/commons/scxml2/model/Final.java     |   32 +-
 .../org/apache/commons/scxml2/model/Foreach.java   |   58 +-
 .../org/apache/commons/scxml2/model/History.java   |   52 +-
 .../java/org/apache/commons/scxml2/model/If.java   |   56 +-
 .../org/apache/commons/scxml2/model/Initial.java   |   44 +-
 .../org/apache/commons/scxml2/model/Invoke.java    |  432 +-
 .../org/apache/commons/scxml2/model/JsonValue.java |   10 +-
 .../java/org/apache/commons/scxml2/model/Log.java  |   36 +-
 .../commons/scxml2/model/ModelException.java       |   14 +-
 .../org/apache/commons/scxml2/model/OnEntry.java   |   20 +-
 .../org/apache/commons/scxml2/model/OnExit.java    |   20 +-
 .../org/apache/commons/scxml2/model/Parallel.java  |   14 +-
 .../org/apache/commons/scxml2/model/Param.java     |   48 +-
 .../commons/scxml2/model/PayloadBuilder.java       |  122 +-
 .../org/apache/commons/scxml2/model/Raise.java     |   28 +-
 .../org/apache/commons/scxml2/model/SCXML.java     |  294 +-
 .../org/apache/commons/scxml2/model/Script.java    |   42 +-
 .../java/org/apache/commons/scxml2/model/Send.java |  474 +--
 .../commons/scxml2/model/SimpleTransition.java     |  164 +-
 .../org/apache/commons/scxml2/model/State.java     |   86 +-
 .../org/apache/commons/scxml2/model/TextValue.java |   10 +-
 .../apache/commons/scxml2/model/Transition.java    |   74 +-
 .../commons/scxml2/model/TransitionTarget.java     |  106 +-
 .../commons/scxml2/model/TransitionalState.java    |  198 +-
 .../java/org/apache/commons/scxml2/model/Var.java  |   52 +-
 .../scxml2/semantics/SCXMLSemanticsImpl.java       | 1426 +++----
 .../org/apache/commons/scxml2/semantics/Step.java  |   26 +-
 .../commons/scxml2/system/EventVariable.java       |   20 +-
 89 files changed, 7289 insertions(+), 7289 deletions(-)

diff --git a/src/main/java/org/apache/commons/scxml2/ActionExecutionContext.java b/src/main/java/org/apache/commons/scxml2/ActionExecutionContext.java
index b402c751..1cd16840 100644
--- a/src/main/java/org/apache/commons/scxml2/ActionExecutionContext.java
+++ b/src/main/java/org/apache/commons/scxml2/ActionExecutionContext.java
@@ -40,17 +40,10 @@ public class ActionExecutionContext {
     }
 
     /**
-     * @return Returns the state machine
-     */
-    public SCXML getStateMachine() {
-        return exctx.getStateMachine();
-    }
-
-    /**
-     * @return Returns the global context
+     * @return Returns the SCXML Execution Logger for the application
      */
-    public Context getGlobalContext() {
-        return exctx.getScInstance().getGlobalContext();
+    public Log getAppLog() {
+        return exctx.getAppLog();
     }
 
     /**
@@ -62,17 +55,17 @@ public class ActionExecutionContext {
     }
 
     /**
-     * @return Returns The evaluator.
+     * @return Returns the error reporter
      */
-    public Evaluator getEvaluator() {
-        return exctx.getEvaluator();
+    public ErrorReporter getErrorReporter() {
+        return exctx.getErrorReporter();
     }
 
     /**
-     * @return Returns the error reporter
+     * @return Returns The evaluator.
      */
-    public ErrorReporter getErrorReporter() {
-        return exctx.getErrorReporter();
+    public Evaluator getEvaluator() {
+        return exctx.getEvaluator();
     }
 
     /**
@@ -82,6 +75,13 @@ public class ActionExecutionContext {
         return exctx.getEventDispatcher();
     }
 
+    /**
+     * @return Returns the global context
+     */
+    public Context getGlobalContext() {
+        return exctx.getScInstance().getGlobalContext();
+    }
+
     /**
      * @return Returns the I/O Processor for the internal event queue
      */
@@ -90,9 +90,9 @@ public class ActionExecutionContext {
     }
 
     /**
-     * @return Returns the SCXML Execution Logger for the application
+     * @return Returns the state machine
      */
-    public Log getAppLog() {
-        return exctx.getAppLog();
+    public SCXML getStateMachine() {
+        return exctx.getStateMachine();
     }
 }
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/scxml2/Context.java b/src/main/java/org/apache/commons/scxml2/Context.java
index 341df93b..ebde03c8 100644
--- a/src/main/java/org/apache/commons/scxml2/Context.java
+++ b/src/main/java/org/apache/commons/scxml2/Context.java
@@ -25,32 +25,35 @@ import java.util.Map;
 public interface Context {
 
     /**
-     * Assigns a new value to an existing variable or creates a new one.
-     * The method searches the chain of parent Contexts for variable
-     * existence.
+     * Gets the value of this variable; delegating to parent.
      *
-     * @param name The variable name
-     * @param value The variable value
+     * @param name The name of the variable
+     * @return The value (or null)
      */
-    void set(String name, Object value);
+    Object get(String name);
 
     /**
-     * Assigns a new value to an existing variable or creates a new one.
-     * The method allows to shaddow a variable of the same name up the
-     * Context chain.
+     * Gets the parent Context, may be null.
      *
-     * @param name The variable name
-     * @param value The variable value
+     * @return The parent Context in a chained Context environment
      */
-    void setLocal(String name, Object value);
+    Context getParent();
 
     /**
-     * Gets the value of this variable; delegating to parent.
+     * Gets the SCXMLSystemContext for this Context, should not be null unless this is the root Context
      *
-     * @param name The name of the variable
-     * @return The value (or null)
+     * @return The SCXMLSystemContext in a chained Context environment
      */
-    Object get(String name);
+    SCXMLSystemContext getSystemContext();
+
+    /**
+     * Gets the Map of all variables in this Context.
+     *
+     * @return Local variable entries Map
+     * To get variables in parent Context, call getParent().getVars().
+     * @see #getParent()
+     */
+    Map<String, Object> getVars();
 
     /**
      * Check if this variable exists, delegating to parent.
@@ -68,32 +71,29 @@ public interface Context {
      */
     boolean hasLocal(String name);
 
-    /**
-     * Gets the Map of all variables in this Context.
-     *
-     * @return Local variable entries Map
-     * To get variables in parent Context, call getParent().getVars().
-     * @see #getParent()
-     */
-    Map<String, Object> getVars();
-
     /**
      * Clear this Context.
      */
     void reset();
 
     /**
-     * Gets the parent Context, may be null.
+     * Assigns a new value to an existing variable or creates a new one.
+     * The method searches the chain of parent Contexts for variable
+     * existence.
      *
-     * @return The parent Context in a chained Context environment
+     * @param name The variable name
+     * @param value The variable value
      */
-    Context getParent();
+    void set(String name, Object value);
 
     /**
-     * Gets the SCXMLSystemContext for this Context, should not be null unless this is the root Context
+     * Assigns a new value to an existing variable or creates a new one.
+     * The method allows to shaddow a variable of the same name up the
+     * Context chain.
      *
-     * @return The SCXMLSystemContext in a chained Context environment
+     * @param name The variable name
+     * @param value The variable value
      */
-    SCXMLSystemContext getSystemContext();
+    void setLocal(String name, Object value);
 
 }
diff --git a/src/main/java/org/apache/commons/scxml2/Evaluator.java b/src/main/java/org/apache/commons/scxml2/Evaluator.java
index f72cc005..357a2821 100644
--- a/src/main/java/org/apache/commons/scxml2/Evaluator.java
+++ b/src/main/java/org/apache/commons/scxml2/Evaluator.java
@@ -31,18 +31,6 @@ public interface Evaluator {
     /** Default Data Model name **/
     String DEFAULT_DATA_MODEL = "";
 
-    /**
-     * Gets the datamodel type supported by this Evaluator
-     * @return The supported datamodel type
-     */
-    String getSupportedDatamodel();
-
-    /**
-     * If this Evaluator only supports a global context.
-     * @return true if this Evaluator only support a global context
-     */
-    boolean requiresGlobalContext();
-
     /**
      * @param data data to be cloned
      * @return A deep clone of the data
@@ -60,6 +48,17 @@ public interface Evaluator {
     Object eval(Context ctx, String expr)
     throws SCXMLExpressionException;
 
+    /**
+     * Assigns data to a location
+     *
+     * @param ctx variable context
+     * @param location location expression
+     * @param data the data to assign.
+     * @throws SCXMLExpressionException A malformed expression exception
+     */
+    void evalAssign(Context ctx, String location, Object data)
+            throws SCXMLExpressionException;
+
     /**
      * Evaluate a condition.
      * Manifests as "cond" attributes of &lt;transition&gt;,
@@ -73,17 +72,6 @@ public interface Evaluator {
     Boolean evalCond(Context ctx, String expr)
     throws SCXMLExpressionException;
 
-    /**
-     * Assigns data to a location
-     *
-     * @param ctx variable context
-     * @param location location expression
-     * @param data the data to assign.
-     * @throws SCXMLExpressionException A malformed expression exception
-     */
-    void evalAssign(Context ctx, String location, Object data)
-            throws SCXMLExpressionException;
-
     /**
      * Evaluate a script.
      * Manifests as &lt;script&gt; element.
@@ -96,6 +84,12 @@ public interface Evaluator {
     Object evalScript(Context ctx, String script)
     throws SCXMLExpressionException;
 
+    /**
+     * Gets the datamodel type supported by this Evaluator
+     * @return The supported datamodel type
+     */
+    String getSupportedDatamodel();
+
     /**
      * Create a new child context.
      *
@@ -104,5 +98,11 @@ public interface Evaluator {
      */
     Context newContext(Context parent);
 
+    /**
+     * If this Evaluator only supports a global context.
+     * @return true if this Evaluator only support a global context
+     */
+    boolean requiresGlobalContext();
+
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/EvaluatorFactory.java b/src/main/java/org/apache/commons/scxml2/EvaluatorFactory.java
index 3886f954..daa77fd3 100644
--- a/src/main/java/org/apache/commons/scxml2/EvaluatorFactory.java
+++ b/src/main/java/org/apache/commons/scxml2/EvaluatorFactory.java
@@ -56,25 +56,27 @@ public class EvaluatorFactory {
 
     private static final EvaluatorFactory INSTANCE = new EvaluatorFactory();
 
-    private final Map<String, EvaluatorProvider> providers = new ConcurrentHashMap<>();
-
-    private EvaluatorFactory() {
-        providers.put(JSEvaluator.SUPPORTED_DATA_MODEL, new JSEvaluator.JSEvaluatorProvider());
-        providers.put(GroovyEvaluator.SUPPORTED_DATA_MODEL, new GroovyEvaluator.GroovyEvaluatorProvider());
-        providers.put(JexlEvaluator.SUPPORTED_DATA_MODEL, new JexlEvaluator.JexlEvaluatorProvider());
-        providers.put(MinimalEvaluator.SUPPORTED_DATA_MODEL, new MinimalEvaluator.MinimalEvaluatorProvider());
-        providers.put(DEFAULT_DATA_MODEL, providers.get(JexlEvaluator.SUPPORTED_DATA_MODEL));
-    }
-
-    public static void setDefaultProvider(final EvaluatorProvider defaultProvider) {
-        INSTANCE.providers.put(DEFAULT_DATA_MODEL, defaultProvider);
-    }
-
     @SuppressWarnings("unused")
     public static EvaluatorProvider getDefaultProvider() {
         return INSTANCE.providers.get(DEFAULT_DATA_MODEL);
     }
 
+    /**
+     * Returns a dedicated Evaluator instance for a specific SCXML document its documentmodel.
+     * <p>If no SCXML document is provided a default Evaluator will be returned.</p>
+     * @param document The document to return a dedicated Evaluator for. May be null to retrieve the default Evaluator.
+     * @return a new and not sharable Evaluator instance for the provided document, or a default Evaluator otherwise
+     * @throws ModelException If the SCXML document datamodel is not supported.
+     */
+    public static Evaluator getEvaluator(final SCXML document) throws ModelException {
+        final String datamodelName = document != null ? document.getDatamodelName() : null;
+        final EvaluatorProvider provider = INSTANCE.providers.get(datamodelName == null ? DEFAULT_DATA_MODEL : datamodelName);
+        if (provider == null) {
+            throw new ModelException("Unsupported SCXML document datamodel \""+(datamodelName)+"\"");
+        }
+        return document != null ? provider.getEvaluator(document) : provider.getEvaluator();
+    }
+
     @SuppressWarnings("unused")
     public static EvaluatorProvider getEvaluatorProvider(final String datamodelName) {
         return INSTANCE.providers.get(datamodelName == null ? DEFAULT_DATA_MODEL : datamodelName);
@@ -85,24 +87,22 @@ public class EvaluatorFactory {
         INSTANCE.providers.put(provider.getSupportedDatamodel(), provider);
     }
 
+    public static void setDefaultProvider(final EvaluatorProvider defaultProvider) {
+        INSTANCE.providers.put(DEFAULT_DATA_MODEL, defaultProvider);
+    }
+
     @SuppressWarnings("unused")
     public static void unregisterEvaluatorProvider(final String datamodelName) {
         INSTANCE.providers.remove(datamodelName == null ? DEFAULT_DATA_MODEL : datamodelName);
     }
 
-    /**
-     * Returns a dedicated Evaluator instance for a specific SCXML document its documentmodel.
-     * <p>If no SCXML document is provided a default Evaluator will be returned.</p>
-     * @param document The document to return a dedicated Evaluator for. May be null to retrieve the default Evaluator.
-     * @return a new and not sharable Evaluator instance for the provided document, or a default Evaluator otherwise
-     * @throws ModelException If the SCXML document datamodel is not supported.
-     */
-    public static Evaluator getEvaluator(final SCXML document) throws ModelException {
-        final String datamodelName = document != null ? document.getDatamodelName() : null;
-        final EvaluatorProvider provider = INSTANCE.providers.get(datamodelName == null ? DEFAULT_DATA_MODEL : datamodelName);
-        if (provider == null) {
-            throw new ModelException("Unsupported SCXML document datamodel \""+(datamodelName)+"\"");
-        }
-        return document != null ? provider.getEvaluator(document) : provider.getEvaluator();
+    private final Map<String, EvaluatorProvider> providers = new ConcurrentHashMap<>();
+
+    private EvaluatorFactory() {
+        providers.put(JSEvaluator.SUPPORTED_DATA_MODEL, new JSEvaluator.JSEvaluatorProvider());
+        providers.put(GroovyEvaluator.SUPPORTED_DATA_MODEL, new GroovyEvaluator.GroovyEvaluatorProvider());
+        providers.put(JexlEvaluator.SUPPORTED_DATA_MODEL, new JexlEvaluator.JexlEvaluatorProvider());
+        providers.put(MinimalEvaluator.SUPPORTED_DATA_MODEL, new MinimalEvaluator.MinimalEvaluatorProvider());
+        providers.put(DEFAULT_DATA_MODEL, providers.get(JexlEvaluator.SUPPORTED_DATA_MODEL));
     }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/EvaluatorProvider.java b/src/main/java/org/apache/commons/scxml2/EvaluatorProvider.java
index b7b7e677..bf32b2ff 100644
--- a/src/main/java/org/apache/commons/scxml2/EvaluatorProvider.java
+++ b/src/main/java/org/apache/commons/scxml2/EvaluatorProvider.java
@@ -23,11 +23,6 @@ import org.apache.commons.scxml2.model.SCXML;
  */
 public interface EvaluatorProvider {
 
-    /**
-     * @return The SCXML datamodel type this provider supports
-     */
-    String getSupportedDatamodel();
-
     /**
      * @return a default or generic {@link Evaluator} supporting the {@link #getSupportedDatamodel()}
      */
@@ -43,4 +38,9 @@ public interface EvaluatorProvider {
      * @return a new and not sharable Evaluator instance
      */
     Evaluator getEvaluator(SCXML document);
+
+    /**
+     * @return The SCXML datamodel type this provider supports
+     */
+    String getSupportedDatamodel();
 }
diff --git a/src/main/java/org/apache/commons/scxml2/EventBuilder.java b/src/main/java/org/apache/commons/scxml2/EventBuilder.java
index d43ec49e..ee97c242 100644
--- a/src/main/java/org/apache/commons/scxml2/EventBuilder.java
+++ b/src/main/java/org/apache/commons/scxml2/EventBuilder.java
@@ -31,43 +31,41 @@ public class EventBuilder {
         this.type = type;
     }
 
-    public String getName() {
-        return name;
+    public TriggerEvent build() {
+        return new TriggerEvent(name, type, sendId, origin, originType, invokeId, data);
     }
 
-    public int getType() {
-        return type;
+    public EventBuilder data(final Object data) {
+        this.data = data;
+        return this;
     }
 
-    public String getSendId() {
-        return sendId;
+    public Object getData() {
+        return data;
     }
 
-    public EventBuilder sendId(final String sendId) {
-        this.sendId = sendId;
-        return this;
+    public String getInvokeId() {
+        return invokeId;
     }
 
-    public String getOrigin() {
-        return origin;
+    public String getName() {
+        return name;
     }
 
-    public EventBuilder origin(final String origin) {
-        this.origin = origin;
-        return this;
+    public String getOrigin() {
+        return origin;
     }
 
     public String getOriginType() {
         return originType;
     }
 
-    public EventBuilder originType(final String originType) {
-        this.originType = originType;
-        return this;
+    public String getSendId() {
+        return sendId;
     }
 
-    public String getInvokeId() {
-        return invokeId;
+    public int getType() {
+        return type;
     }
 
     public EventBuilder invokeId(final String invokeId) {
@@ -75,16 +73,18 @@ public class EventBuilder {
         return this;
     }
 
-    public Object getData() {
-        return data;
+    public EventBuilder origin(final String origin) {
+        this.origin = origin;
+        return this;
     }
 
-    public EventBuilder data(final Object data) {
-        this.data = data;
+    public EventBuilder originType(final String originType) {
+        this.originType = originType;
         return this;
     }
 
-    public TriggerEvent build() {
-        return new TriggerEvent(name, type, sendId, origin, originType, invokeId, data);
+    public EventBuilder sendId(final String sendId) {
+        this.sendId = sendId;
+        return this;
     }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/EventDispatcher.java b/src/main/java/org/apache/commons/scxml2/EventDispatcher.java
index dfe0e1f0..6a87d068 100644
--- a/src/main/java/org/apache/commons/scxml2/EventDispatcher.java
+++ b/src/main/java/org/apache/commons/scxml2/EventDispatcher.java
@@ -28,6 +28,13 @@ import org.apache.commons.scxml2.invoke.Invoker;
  */
 public interface EventDispatcher {
 
+    /**
+     * Cancel the specified send message.
+     *
+     * @param sendId The ID of the send message to cancel
+     */
+    void cancel(String sendId);
+
     /**
      * A EventDispatcher keeps track of outstanding (pending) events to be send on behalf of the statemachine
      * it is 'attached' to.
@@ -38,13 +45,6 @@ public interface EventDispatcher {
      */
     EventDispatcher newInstance();
 
-    /**
-     * Cancel the specified send message.
-     *
-     * @param sendId The ID of the send message to cancel
-     */
-    void cancel(String sendId);
-
     /**
      * Send this message to the target.
      *
diff --git a/src/main/java/org/apache/commons/scxml2/NotificationRegistry.java b/src/main/java/org/apache/commons/scxml2/NotificationRegistry.java
index ff052fe4..5f20fe4f 100644
--- a/src/main/java/org/apache/commons/scxml2/NotificationRegistry.java
+++ b/src/main/java/org/apache/commons/scxml2/NotificationRegistry.java
@@ -61,24 +61,6 @@ public final class NotificationRegistry {
         }
     }
 
-    /**
-     * Deregister this SCXMLListener for this Observable.
-     *
-     * @param source The observable this listener wants to stop listening to
-     * @param lst The listener
-     */
-    synchronized void removeListener(final Observable source, final SCXMLListener lst) {
-        if (source != null && source.getObservableId() != null) {
-            final Set<SCXMLListener> entries = regs.get(source.getObservableId());
-            if (entries != null) {
-                entries.remove(lst);
-                if (entries.isEmpty()) {
-                    regs.remove(source.getObservableId());
-                }
-            }
-        }
-    }
-
     /**
      * Inform all relevant listeners that a EnterableState has been
      * entered.
@@ -138,5 +120,23 @@ public final class NotificationRegistry {
             }
         }
     }
+
+    /**
+     * Deregister this SCXMLListener for this Observable.
+     *
+     * @param source The observable this listener wants to stop listening to
+     * @param lst The listener
+     */
+    synchronized void removeListener(final Observable source, final SCXMLListener lst) {
+        if (source != null && source.getObservableId() != null) {
+            final Set<SCXMLListener> entries = regs.get(source.getObservableId());
+            if (entries != null) {
+                entries.remove(lst);
+                if (entries.isEmpty()) {
+                    regs.remove(source.getObservableId());
+                }
+            }
+        }
+    }
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/ParentSCXMLIOProcessor.java b/src/main/java/org/apache/commons/scxml2/ParentSCXMLIOProcessor.java
index f3cc08c6..f0a6dffc 100644
--- a/src/main/java/org/apache/commons/scxml2/ParentSCXMLIOProcessor.java
+++ b/src/main/java/org/apache/commons/scxml2/ParentSCXMLIOProcessor.java
@@ -33,14 +33,14 @@ public class ParentSCXMLIOProcessor implements SCXMLIOProcessor {
         }
     }
 
-    public String getInvokeId() {
-        return invokeId;
-    }
-
     public synchronized void close() {
         processor = null;
     }
 
+    public String getInvokeId() {
+        return invokeId;
+    }
+
     public synchronized boolean isClosed() {
         return processor == null;
     }
diff --git a/src/main/java/org/apache/commons/scxml2/PathResolver.java b/src/main/java/org/apache/commons/scxml2/PathResolver.java
index 979854ad..3b0aee15 100644
--- a/src/main/java/org/apache/commons/scxml2/PathResolver.java
+++ b/src/main/java/org/apache/commons/scxml2/PathResolver.java
@@ -23,20 +23,20 @@ package org.apache.commons.scxml2;
 public interface PathResolver {
 
     /**
-     * Resolve this context sensitive path to an absolute URL.
+     * Gets a PathResolver rooted at this context sensitive path.
      *
      * @param ctxPath Context sensitive path, can be a relative URL
-     * @return Resolved path (an absolute URL) or <code>null</code>
+     * @return Returns a new resolver rooted at ctxPath
      */
-    String resolvePath(String ctxPath);
+    PathResolver getResolver(String ctxPath);
 
     /**
-     * Gets a PathResolver rooted at this context sensitive path.
+     * Resolve this context sensitive path to an absolute URL.
      *
      * @param ctxPath Context sensitive path, can be a relative URL
-     * @return Returns a new resolver rooted at ctxPath
+     * @return Resolved path (an absolute URL) or <code>null</code>
      */
-    PathResolver getResolver(String ctxPath);
+    String resolvePath(String ctxPath);
 
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/SCInstance.java b/src/main/java/org/apache/commons/scxml2/SCInstance.java
index a3f80640..3d678adf 100644
--- a/src/main/java/org/apache/commons/scxml2/SCInstance.java
+++ b/src/main/java/org/apache/commons/scxml2/SCInstance.java
@@ -145,158 +145,6 @@ public class SCInstance implements Serializable {
         this.currentStatus = new Status(stateConfiguration);
     }
 
-    /**
-     * (re)Initializes the state machine instance, clearing all variable contexts, histories and current status,
-     * and clones the SCXML root datamodel into the root context.
-     * @throws ModelException if the state machine hasn't been setup for this instance
-     */
-    protected void initialize() throws ModelException {
-        running = false;
-        if (stateMachine == null) {
-            throw new ModelException(ERR_NO_STATE_MACHINE);
-        }
-        if (evaluator == null) {
-            evaluator = EvaluatorFactory.getEvaluator(stateMachine);
-        }
-        if (evaluator.requiresGlobalContext()) {
-            singleContext = true;
-        }
-        if (stateMachine.getDatamodelName() != null && !stateMachine.getDatamodelName().equals(evaluator.getSupportedDatamodel())) {
-            throw new ModelException("Incompatible SCXML document datamodel \""+stateMachine.getDatamodelName()+"\""
-                    + " for evaluator "+evaluator.getClass().getName()+" supported datamodel \""+evaluator.getSupportedDatamodel()+"\"");
-        }
-        if (errorReporter == null) {
-            throw new ModelException(ERR_NO_ERROR_REPORTER);
-        }
-        systemContext = null;
-        globalContext = null;
-        contexts.clear();
-        histories.clear();
-        stateConfiguration.clear();
-
-        initialized = true;
-    }
-
-    protected void initializeDatamodel(final Map<String, Object> data) {
-        if (globalContext == null) {
-            // Clone root datamodel
-            final Datamodel rootdm = stateMachine.getDatamodel();
-            cloneDatamodel(rootdm, getGlobalContext(), evaluator, errorReporter);
-
-            // initialize/override global context data
-            if (data != null) {
-                for (final String key : data.keySet()) {
-                    if (globalContext.has(key)) {
-                        globalContext.set(key, data.get(key));
-                    }
-                }
-            }
-            if (stateMachine.isLateBinding() == null || Boolean.FALSE.equals(stateMachine.isLateBinding())) {
-                // early binding
-                for (final EnterableState es : stateMachine.getChildren()) {
-                    getContext(es);
-                }
-            }
-        }
-    }
-
-    /**
-     * Detach this state machine instance to allow external serialization.
-     * <p>
-     * This clears the internal I/O processor, evaluator and errorReporter members.
-     * </p>
-     */
-    protected void detach() {
-        this.internalIOProcessor = null;
-        this.evaluator = null;
-        this.errorReporter = null;
-    }
-
-    /**
-     * Sets the I/O Processor for the internal event queue
-     * @param internalIOProcessor the I/O Processor
-     */
-    protected void setInternalIOProcessor(final SCXMLIOProcessor internalIOProcessor) {
-        this.internalIOProcessor = internalIOProcessor;
-    }
-
-    /**
-     * Sets or re-attach the evaluator
-     * <p>
-     * If not re-attaching and this state machine instance has been initialized before,
-     * it will be initialized again, destroying all existing state!
-     * </p>
-     * @param evaluator The evaluator for this state machine instance
-     * @param reAttach Flag whether or not re-attaching it
-     * @throws ModelException if {@code evaluator} is null
-     */
-    protected void setEvaluator(final Evaluator evaluator, final boolean reAttach) throws ModelException {
-        this.evaluator = evaluator;
-        if (initialized) {
-            if (!reAttach) {
-                // change of evaluator after initialization: re-initialize
-                initialize();
-            }
-            else if (evaluator == null) {
-                throw new ModelException("SCInstance: re-attached without Evaluator");
-            }
-        }
-    }
-
-    /**
-     * @return Return the current evaluator
-     */
-    protected Evaluator getEvaluator() {
-        return evaluator;
-    }
-
-    /**
-     * Sets or re-attach the error reporter
-     * @param errorReporter The error reporter for this state machine instance.
-     * @throws ModelException if an attempt is made to set a null value for the error reporter
-     */
-    protected void setErrorReporter(final ErrorReporter errorReporter) throws ModelException {
-        if (errorReporter == null) {
-            throw new ModelException(ERR_NO_ERROR_REPORTER);
-        }
-        this.errorReporter = errorReporter;
-    }
-
-    /**
-     * @return Return the state machine for this instance
-     */
-    public SCXML getStateMachine() {
-        return stateMachine;
-    }
-
-    /**
-     * Sets the state machine for this instance.
-     * <p>
-     * If this state machine instance has been initialized before, it will be initialized again, destroying all existing
-     * state!
-     * </p>
-     * @param stateMachine The state machine for this instance
-     * @throws ModelException if an attempt is made to set a null value for the state machine
-     */
-    protected void setStateMachine(final SCXML stateMachine) throws ModelException {
-        if (stateMachine == null) {
-            throw new ModelException(ERR_NO_STATE_MACHINE);
-        }
-        this.stateMachine = stateMachine;
-        initialize();
-    }
-
-    public void setSingleContext(final boolean singleContext) throws ModelException {
-        if (initialized) {
-            throw new ModelException("SCInstance: already initialized");
-        }
-        this.singleContext = singleContext;
-    }
-
-    public boolean isSingleContext() {
-        return singleContext;
-    }
-
     /**
      * Clone data model.
      *
@@ -378,10 +226,45 @@ public class SCInstance implements Serializable {
     }
 
     /**
-     * @return Returns the state configuration for this instance
+     * Detach this state machine instance to allow external serialization.
+     * <p>
+     * This clears the internal I/O processor, evaluator and errorReporter members.
+     * </p>
      */
-    public StateConfiguration getStateConfiguration() {
-        return stateConfiguration;
+    protected void detach() {
+        this.internalIOProcessor = null;
+        this.evaluator = null;
+        this.errorReporter = null;
+    }
+
+    /**
+     * Gets the context for an EnterableState or create one if not created before.
+     *
+     * @param state The EnterableState.
+     * @return The context.
+     */
+    public Context getContext(final EnterableState state) {
+        Context context = contexts.get(state);
+        if (context == null) {
+            if (singleContext) {
+                context = getGlobalContext();
+            }
+            else {
+                final EnterableState parent = state.getParent();
+                if (parent == null) {
+                    // docroot
+                    context = evaluator.newContext(getGlobalContext());
+                } else {
+                    context = evaluator.newContext(getContext(parent));
+                }
+            }
+            if (state instanceof TransitionalState) {
+                final Datamodel datamodel = ((TransitionalState)state).getDatamodel();
+                cloneDatamodel(datamodel, context, evaluator, errorReporter);
+            }
+            contexts.put(state, context);
+        }
+        return context;
     }
 
     /**
@@ -392,28 +275,38 @@ public class SCInstance implements Serializable {
     }
 
     /**
-     * @return Returns if the state machine is running
+     * @return Return the current evaluator
      */
-    public boolean isRunning() {
-        return running;
+    protected Evaluator getEvaluator() {
+        return evaluator;
     }
 
     /**
-     * Starts the state machine, {@link #isRunning()} hereafter will return true
-     * @throws IllegalStateException Exception thrown if trying to start the state machine when in a Final state
+     * @return Returns the global context, which is the top context <em>within</em> the state machine.
      */
-    public void start() throws IllegalStateException {
-        if (!this.running &&  currentStatus.isFinal()) {
-            throw new IllegalStateException("The state machine is in a Final state and cannot be set running again");
+    public Context getGlobalContext() {
+        if (globalContext == null) {
+            // force initialization of systemContext
+            getSystemContext();
+            if (systemContext != null) {
+                globalContext = evaluator.newContext(systemContext);
+            }
         }
-        this.running = true;
+        return globalContext;
     }
 
     /**
-     * Stops the state machine, {@link #isRunning()} hereafter will return false
+     * Gets the last configuration for this history.
+     *
+     * @param history The history.
+     * @return Returns the lastConfiguration.
      */
-    public void stop() {
-        this.running = false;
+    public Set<EnterableState> getLastConfiguration(final History history) {
+        Set<EnterableState> lastConfiguration = histories.get(history);
+        if (lastConfiguration == null) {
+            lastConfiguration = Collections.emptySet();
+        }
+        return lastConfiguration;
     }
 
     /**
@@ -429,17 +322,17 @@ public class SCInstance implements Serializable {
     }
 
     /**
-     * Sets or replace the root context.
-     * @param context The new root context.
+     * @return Returns the state configuration for this instance
      */
-    protected void setRootContext(final Context context) {
-        this.rootContext = context;
-        // force initialization of rootContext
-        getRootContext();
-        if (systemContext != null) {
-            // re-parent the system context
-            systemContext.setSystemContext(new SimpleContext(rootContext));
-        }
+    public StateConfiguration getStateConfiguration() {
+        return stateConfiguration;
+    }
+
+    /**
+     * @return Return the state machine for this instance
+     */
+    public SCXML getStateMachine() {
+        return stateMachine;
     }
 
     /**
@@ -464,47 +357,69 @@ public class SCInstance implements Serializable {
     }
 
     /**
-     * @return Returns the global context, which is the top context <em>within</em> the state machine.
+     * (re)Initializes the state machine instance, clearing all variable contexts, histories and current status,
+     * and clones the SCXML root datamodel into the root context.
+     * @throws ModelException if the state machine hasn't been setup for this instance
      */
-    public Context getGlobalContext() {
-        if (globalContext == null) {
-            // force initialization of systemContext
-            getSystemContext();
-            if (systemContext != null) {
-                globalContext = evaluator.newContext(systemContext);
-            }
+    protected void initialize() throws ModelException {
+        running = false;
+        if (stateMachine == null) {
+            throw new ModelException(ERR_NO_STATE_MACHINE);
         }
-        return globalContext;
+        if (evaluator == null) {
+            evaluator = EvaluatorFactory.getEvaluator(stateMachine);
+        }
+        if (evaluator.requiresGlobalContext()) {
+            singleContext = true;
+        }
+        if (stateMachine.getDatamodelName() != null && !stateMachine.getDatamodelName().equals(evaluator.getSupportedDatamodel())) {
+            throw new ModelException("Incompatible SCXML document datamodel \""+stateMachine.getDatamodelName()+"\""
+                    + " for evaluator "+evaluator.getClass().getName()+" supported datamodel \""+evaluator.getSupportedDatamodel()+"\"");
+        }
+        if (errorReporter == null) {
+            throw new ModelException(ERR_NO_ERROR_REPORTER);
+        }
+        systemContext = null;
+        globalContext = null;
+        contexts.clear();
+        histories.clear();
+        stateConfiguration.clear();
+
+        initialized = true;
     }
 
-    /**
-     * Gets the context for an EnterableState or create one if not created before.
-     *
-     * @param state The EnterableState.
-     * @return The context.
-     */
-    public Context getContext(final EnterableState state) {
-        Context context = contexts.get(state);
-        if (context == null) {
-            if (singleContext) {
-                context = getGlobalContext();
-            }
-            else {
-                final EnterableState parent = state.getParent();
-                if (parent == null) {
-                    // docroot
-                    context = evaluator.newContext(getGlobalContext());
-                } else {
-                    context = evaluator.newContext(getContext(parent));
+    protected void initializeDatamodel(final Map<String, Object> data) {
+        if (globalContext == null) {
+            // Clone root datamodel
+            final Datamodel rootdm = stateMachine.getDatamodel();
+            cloneDatamodel(rootdm, getGlobalContext(), evaluator, errorReporter);
+
+            // initialize/override global context data
+            if (data != null) {
+                for (final String key : data.keySet()) {
+                    if (globalContext.has(key)) {
+                        globalContext.set(key, data.get(key));
+                    }
                 }
             }
-            if (state instanceof TransitionalState) {
-                final Datamodel datamodel = ((TransitionalState)state).getDatamodel();
-                cloneDatamodel(datamodel, context, evaluator, errorReporter);
+            if (stateMachine.isLateBinding() == null || Boolean.FALSE.equals(stateMachine.isLateBinding())) {
+                // early binding
+                for (final EnterableState es : stateMachine.getChildren()) {
+                    getContext(es);
+                }
             }
-            contexts.put(state, context);
         }
-        return context;
+    }
+
+    /**
+     * @return Returns if the state machine is running
+     */
+    public boolean isRunning() {
+        return running;
+    }
+
+    public boolean isSingleContext() {
+        return singleContext;
     }
 
     /**
@@ -519,6 +434,17 @@ public class SCInstance implements Serializable {
         return contexts.get(state);
     }
 
+    /**
+     * Resets the history state.
+     *
+     * <p>Note: used for testing purposes only</p>
+     *
+     * @param history The history.
+     */
+    public void resetConfiguration(final History history) {
+        histories.remove(history);
+    }
+
     /**
      * Sets the context for an EnterableState
      *
@@ -533,17 +459,46 @@ public class SCInstance implements Serializable {
     }
 
     /**
-     * Gets the last configuration for this history.
-     *
-     * @param history The history.
-     * @return Returns the lastConfiguration.
+     * Sets or re-attach the error reporter
+     * @param errorReporter The error reporter for this state machine instance.
+     * @throws ModelException if an attempt is made to set a null value for the error reporter
      */
-    public Set<EnterableState> getLastConfiguration(final History history) {
-        Set<EnterableState> lastConfiguration = histories.get(history);
-        if (lastConfiguration == null) {
-            lastConfiguration = Collections.emptySet();
+    protected void setErrorReporter(final ErrorReporter errorReporter) throws ModelException {
+        if (errorReporter == null) {
+            throw new ModelException(ERR_NO_ERROR_REPORTER);
         }
-        return lastConfiguration;
+        this.errorReporter = errorReporter;
+    }
+
+    /**
+     * Sets or re-attach the evaluator
+     * <p>
+     * If not re-attaching and this state machine instance has been initialized before,
+     * it will be initialized again, destroying all existing state!
+     * </p>
+     * @param evaluator The evaluator for this state machine instance
+     * @param reAttach Flag whether or not re-attaching it
+     * @throws ModelException if {@code evaluator} is null
+     */
+    protected void setEvaluator(final Evaluator evaluator, final boolean reAttach) throws ModelException {
+        this.evaluator = evaluator;
+        if (initialized) {
+            if (!reAttach) {
+                // change of evaluator after initialization: re-initialize
+                initialize();
+            }
+            else if (evaluator == null) {
+                throw new ModelException("SCInstance: re-attached without Evaluator");
+            }
+        }
+    }
+
+    /**
+     * Sets the I/O Processor for the internal event queue
+     * @param internalIOProcessor the I/O Processor
+     */
+    protected void setInternalIOProcessor(final SCXMLIOProcessor internalIOProcessor) {
+        this.internalIOProcessor = internalIOProcessor;
     }
 
     /**
@@ -558,14 +513,59 @@ public class SCInstance implements Serializable {
     }
 
     /**
-     * Resets the history state.
-     *
-     * <p>Note: used for testing purposes only</p>
-     *
-     * @param history The history.
+     * Sets or replace the root context.
+     * @param context The new root context.
      */
-    public void resetConfiguration(final History history) {
-        histories.remove(history);
+    protected void setRootContext(final Context context) {
+        this.rootContext = context;
+        // force initialization of rootContext
+        getRootContext();
+        if (systemContext != null) {
+            // re-parent the system context
+            systemContext.setSystemContext(new SimpleContext(rootContext));
+        }
+    }
+
+    public void setSingleContext(final boolean singleContext) throws ModelException {
+        if (initialized) {
+            throw new ModelException("SCInstance: already initialized");
+        }
+        this.singleContext = singleContext;
+    }
+
+    /**
+     * Sets the state machine for this instance.
+     * <p>
+     * If this state machine instance has been initialized before, it will be initialized again, destroying all existing
+     * state!
+     * </p>
+     * @param stateMachine The state machine for this instance
+     * @throws ModelException if an attempt is made to set a null value for the state machine
+     */
+    protected void setStateMachine(final SCXML stateMachine) throws ModelException {
+        if (stateMachine == null) {
+            throw new ModelException(ERR_NO_STATE_MACHINE);
+        }
+        this.stateMachine = stateMachine;
+        initialize();
+    }
+
+    /**
+     * Starts the state machine, {@link #isRunning()} hereafter will return true
+     * @throws IllegalStateException Exception thrown if trying to start the state machine when in a Final state
+     */
+    public void start() throws IllegalStateException {
+        if (!this.running &&  currentStatus.isFinal()) {
+            throw new IllegalStateException("The state machine is in a Final state and cannot be set running again");
+        }
+        this.running = true;
+    }
+
+    /**
+     * Stops the state machine, {@link #isRunning()} hereafter will return false
+     */
+    public void stop() {
+        this.running = false;
     }
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/SCInstanceObjectInputStream.java b/src/main/java/org/apache/commons/scxml2/SCInstanceObjectInputStream.java
index 98adec97..67e5e5a5 100644
--- a/src/main/java/org/apache/commons/scxml2/SCInstanceObjectInputStream.java
+++ b/src/main/java/org/apache/commons/scxml2/SCInstanceObjectInputStream.java
@@ -57,6 +57,14 @@ public class SCInstanceObjectInputStream extends ObjectInputStream {
         super(in);
     }
 
+    @Override
+    protected Class resolveClass(final ObjectStreamClass osc) throws IOException, ClassNotFoundException {
+        if (classResolver != null) {
+            return classResolver.resolveClass(osc);
+        }
+        return super.resolveClass(osc);
+    }
+
     /**
      * Sets custom class resolver callback, or null when no longer needed.
      * <p>
@@ -86,12 +94,4 @@ public class SCInstanceObjectInputStream extends ObjectInputStream {
         this.classResolver = classResolver;
         return old;
     }
-
-    @Override
-    protected Class resolveClass(final ObjectStreamClass osc) throws IOException, ClassNotFoundException {
-        if (classResolver != null) {
-            return classResolver.resolveClass(osc);
-        }
-        return super.resolveClass(osc);
-    }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/SCXMLExecutionContext.java b/src/main/java/org/apache/commons/scxml2/SCXMLExecutionContext.java
index cce5565c..a36d929f 100644
--- a/src/main/java/org/apache/commons/scxml2/SCXMLExecutionContext.java
+++ b/src/main/java/org/apache/commons/scxml2/SCXMLExecutionContext.java
@@ -160,81 +160,81 @@ public class SCXMLExecutionContext implements SCXMLIOProcessor {
         registerInvokerClass(SCXML_INVOKER_TYPE, SimpleSCXMLInvoker.class);
     }
 
-    public SCXMLExecutor getSCXMLExecutor() {
-        return scxmlExecutor;
-    }
-
-    public SCXMLIOProcessor getExternalIOProcessor() {
-        return externalIOProcessor;
-    }
-
-    public SCXMLIOProcessor getInternalIOProcessor() {
-        return this;
-    }
-
-    /**
-     * @return Returns the restricted execution context for actions
-     */
-    public ActionExecutionContext getActionExecutionContext() {
-        return actionExecutionContext;
-    }
-
-    /**
-     * Sets if the SCXML configuration should be checked before execution (default = true)
-     * @param checkLegalConfiguration flag to set
-     */
-    public void setCheckLegalConfiguration(final boolean checkLegalConfiguration) {
-        this.checkLegalConfiguration = checkLegalConfiguration;
-    }
-
     /**
-     * @return if the SCXML configuration will be checked before execution
+     * Add an event to the internal event queue
+     * @param event The event
      */
-    public boolean isCheckLegalConfiguration() {
-        return checkLegalConfiguration;
+    @Override
+    public void addEvent(final TriggerEvent event) {
+        internalEventQueue.add(event);
     }
 
     /**
-     * Initialize will cancel all current active Invokers, clear the internal event queue and (re)initialize the state machine
-     * @param data input model map to initialize the data model with
-     * @throws ModelException if the state machine hasn't been setup for the internal SCXML instance
+     * Re-attach a previously detached SCInstance.
+     * <p>
+     * Note: an already attached instance will get overwritten (and thus lost).
+     * </p>
+     * @param instance An previously detached SCInstance
      */
-    public void initialize(final Map<String, Object> data) throws ModelException {
-        if (!invokeIds.isEmpty()) {
-            for (final Invoke invoke : new ArrayList<>(invokeIds.keySet())) {
-                cancelInvoker(invoke);
+    protected void attachInstance(final SCInstance instance) {
+        if (scInstance != null ) {
+            scInstance.detach();
+        }
+        scInstance = instance;
+        if (scInstance != null) {
+            scInstance.detach();
+            try {
+                scInstance.setInternalIOProcessor(this);
+                scInstance.setEvaluator(evaluator, true);
+                scInstance.setErrorReporter(errorReporter);
+                initializeIOProcessors();
+            }
+            catch (final ModelException me) {
+                // should not happen
             }
         }
-        internalEventQueue.clear();
-        scInstance.initialize();
-        initializeIOProcessors();
-        scInstance.initializeDatamodel(data);
     }
+
     /**
-     * (re)start the state machine.
+     * Cancel and remove an active Invoker
+     *
+     * @param invoke The Invoke for the Invoker to cancel
      */
-    public void start() {
-        if (scInstance.isRunning()) {
-            throw new IllegalStateException("The state machine has already started.");
-        }
-        if (scInstance.getGlobalContext() == null) {
-            throw new IllegalStateException("The state machine has not been initialized yet.");
+    public void cancelInvoker(final Invoke invoke) {
+        final String invokeId = invokeIds.get(invoke);
+        if (invokeId != null) {
+            try {
+                invokers.get(invokeId).cancel();
+            } catch (final InvokerException ie) {
+                final TriggerEvent te = new EventBuilder("failed.invoke.cancel."+invokeId, TriggerEvent.ERROR_EVENT).build();
+                addEvent(te);
+            }
+            removeInvoker(invoke);
         }
-        scInstance.start();
     }
 
     /**
-     * @return Returns true if this state machine is running
+     * Detach the current SCInstance to allow external serialization.
+     * <p>
+     * {@link #attachInstance(SCInstance)} can be used to re-attach a previously detached instance
+     * </p>
+     * @return the detached instance
      */
-    public boolean isRunning() {
-        return scInstance.isRunning();
+    protected SCInstance detachInstance() {
+        final SCInstance instance = scInstance;
+        scInstance.detach();
+        final Map<String, Object> systemVars = scInstance.getSystemContext().getVars();
+        systemVars.remove(SCXMLSystemContext.IOPROCESSORS_KEY);
+        systemVars.remove(SCXMLSystemContext.EVENT_KEY);
+        scInstance = null;
+        return instance;
     }
 
     /**
-     * Stop the state machine
+     * @return Returns the restricted execution context for actions
      */
-    public void stop() {
-        scInstance.stop();
+    public ActionExecutionContext getActionExecutionContext() {
+        return actionExecutionContext;
     }
 
     /**
@@ -245,101 +245,100 @@ public class SCXMLExecutionContext implements SCXMLIOProcessor {
     }
 
     /**
-     * @return Returns the state machine
+     * @return Returns the error reporter
      */
-    public SCXML getStateMachine() {
-        return scInstance.getStateMachine();
+    public ErrorReporter getErrorReporter() {
+        return errorReporter;
     }
-
     /**
-     * Sets or replace the state machine to be executed
-     * <p>
-     * If the state machine instance has been initialized before, it will be initialized again, destroying all existing
-     * state!
-     * </p>
-     * @param stateMachine The state machine to set
-     * @throws ModelException if attempting to set a null value or the state machine instance failed to re-initialize
+     * @return Returns The evaluator.
      */
-    protected void setStateMachine(final SCXML stateMachine) throws ModelException {
-        scInstance.setStateMachine(stateMachine);
-        // synchronize possible derived evaluator
-        this.evaluator = scInstance.getEvaluator();
-        initializeIOProcessors();
+    public Evaluator getEvaluator() {
+        return evaluator;
     }
 
     /**
-     * @return Returns the SCInstance
+     * @return Returns the event dispatcher
      */
-    public SCInstance getScInstance() {
-        return scInstance;
+    public EventDispatcher getEventDispatcher() {
+        return eventdispatcher;
+    }
+
+    public SCXMLIOProcessor getExternalIOProcessor() {
+        return externalIOProcessor;
+    }
+
+    public SCXMLIOProcessor getInternalIOProcessor() {
+        return this;
     }
 
     /**
-     * @return Returns The evaluator.
+     * @return Returns the map of current active Invokes and their invokeId
      */
-    public Evaluator getEvaluator() {
-        return evaluator;
+    public Map<Invoke, String> getInvokeIds() {
+        return invokeIds;
     }
 
     /**
-     * Sets or replace the evaluator
-     * <p>
-     * If the state machine instance has been initialized before, it will be initialized again, destroying all existing
-     * state!
-     * </p>
-     * @param evaluator The evaluator to set
-     * @throws ModelException if attempting to set a null value or the state machine instance failed to re-initialize
+     * Gets the {@link Invoker} for this {@link Invoke}.
+     * May return <code>null</code>. A non-null {@link Invoker} will be
+     * returned if and only if the {@link Invoke} parent TransitionalState is
+     * currently active and contains the &lt;invoke&gt; child.
+     *
+     * @param invoke The <code>Invoke</code>.
+     * @return The Invoker.
      */
-    protected void setEvaluator(final Evaluator evaluator) throws ModelException {
-        scInstance.setEvaluator(evaluator, false);
-        // synchronize possible derived evaluator
-        this.evaluator = scInstance.getEvaluator();
-        initializeIOProcessors();
+    public Invoker getInvoker(final Invoke invoke) {
+        return invokers.get(invokeIds.get(invoke));
     }
 
     /**
-     * @return Returns the error reporter
+     * @return Returns the notification registry
      */
-    public ErrorReporter getErrorReporter() {
-        return errorReporter;
+    public NotificationRegistry getNotificationRegistry() {
+        return notificationRegistry;
     }
 
     /**
-     * Sets or replace the error reporter
-     *
-     * @param errorReporter The error reporter to set, if null a SimpleErrorReporter instance will be used instead
+     * @return Returns the SCInstance
      */
-    protected void setErrorReporter(final ErrorReporter errorReporter) {
-        this.errorReporter = errorReporter != null ? errorReporter : new SimpleErrorReporter();
-        try {
-            scInstance.setErrorReporter(errorReporter);
-        }
-        catch (final ModelException me) {
-            // won't happen
-        }
+    public SCInstance getScInstance() {
+        return scInstance;
+    }
+
+    public SCXMLExecutor getSCXMLExecutor() {
+        return scxmlExecutor;
     }
 
     /**
-     * @return Returns the event dispatcher
+     * @return Returns the state machine
      */
-    public EventDispatcher getEventDispatcher() {
-        return eventdispatcher;
+    public SCXML getStateMachine() {
+        return scInstance.getStateMachine();
     }
 
     /**
-     * Sets or replace the event dispatch
-     *
-     * @param eventdispatcher The event dispatcher to set, if null a SimpleDispatcher instance will be used instead
+     * @return Returns true if the internal event queue isn't empty
      */
-    protected void setEventdispatcher(final EventDispatcher eventdispatcher) {
-        this.eventdispatcher = eventdispatcher != null ? eventdispatcher : new SimpleDispatcher();
+    public boolean hasPendingInternalEvent() {
+        return !internalEventQueue.isEmpty();
     }
 
     /**
-     * @return Returns the notification registry
+     * Initialize will cancel all current active Invokers, clear the internal event queue and (re)initialize the state machine
+     * @param data input model map to initialize the data model with
+     * @throws ModelException if the state machine hasn't been setup for the internal SCXML instance
      */
-    public NotificationRegistry getNotificationRegistry() {
-        return notificationRegistry;
+    public void initialize(final Map<String, Object> data) throws ModelException {
+        if (!invokeIds.isEmpty()) {
+            for (final Invoke invoke : new ArrayList<>(invokeIds.keySet())) {
+                cancelInvoker(invoke);
+            }
+        }
+        internalEventQueue.clear();
+        scInstance.initialize();
+        initializeIOProcessors();
+        scInstance.initializeDatamodel(data);
     }
 
     /**
@@ -362,75 +361,17 @@ public class SCXMLExecutionContext implements SCXMLIOProcessor {
     }
 
     /**
-     * Detach the current SCInstance to allow external serialization.
-     * <p>
-     * {@link #attachInstance(SCInstance)} can be used to re-attach a previously detached instance
-     * </p>
-     * @return the detached instance
-     */
-    protected SCInstance detachInstance() {
-        final SCInstance instance = scInstance;
-        scInstance.detach();
-        final Map<String, Object> systemVars = scInstance.getSystemContext().getVars();
-        systemVars.remove(SCXMLSystemContext.IOPROCESSORS_KEY);
-        systemVars.remove(SCXMLSystemContext.EVENT_KEY);
-        scInstance = null;
-        return instance;
-    }
-
-    /**
-     * Re-attach a previously detached SCInstance.
-     * <p>
-     * Note: an already attached instance will get overwritten (and thus lost).
-     * </p>
-     * @param instance An previously detached SCInstance
-     */
-    protected void attachInstance(final SCInstance instance) {
-        if (scInstance != null ) {
-            scInstance.detach();
-        }
-        scInstance = instance;
-        if (scInstance != null) {
-            scInstance.detach();
-            try {
-                scInstance.setInternalIOProcessor(this);
-                scInstance.setEvaluator(evaluator, true);
-                scInstance.setErrorReporter(errorReporter);
-                initializeIOProcessors();
-            }
-            catch (final ModelException me) {
-                // should not happen
-            }
-        }
-    }
-
-    /**
-     * Trivial utility method needed for SCXML IRP test 216 which (IMO incorrectly uses http://www.w3.org/TR/scxml
-     * (no trailing /) while the SCXML spec explicitly states http://www.w3.org/TR/scxml/ should be used (supported)
-     * @param uri
-     * @return
-     */
-    private String stripTrailingSlash(final String uri) {
-        return uri.endsWith("/") ? uri.substring(0, uri.length()-1) : uri;
-    }
-
-    /**
-     * Register an Invoker for this target type.
-     *
-     * @param type The target type (specified by "type" attribute of the invoke element).
-     * @param invokerClass The Invoker class.
+     * @return if the SCXML configuration will be checked before execution
      */
-    protected void registerInvokerClass(final String type, final Class<? extends Invoker> invokerClass) {
-        invokerClasses.put(stripTrailingSlash(type), invokerClass);
+    public boolean isCheckLegalConfiguration() {
+        return checkLegalConfiguration;
     }
 
     /**
-     * Remove the Invoker registered for this target type (if there is one registered).
-     *
-     * @param type The target type (specified by "type" attribute of the invoke element).
+     * @return Returns true if this state machine is running
      */
-    protected void unregisterInvokerClass(final String type) {
-        invokerClasses.remove(stripTrailingSlash(type));
+    public boolean isRunning() {
+        return scInstance.isRunning();
     }
 
     /**
@@ -455,16 +396,10 @@ public class SCXMLExecutionContext implements SCXMLIOProcessor {
     }
 
     /**
-     * Gets the {@link Invoker} for this {@link Invoke}.
-     * May return <code>null</code>. A non-null {@link Invoker} will be
-     * returned if and only if the {@link Invoke} parent TransitionalState is
-     * currently active and contains the &lt;invoke&gt; child.
-     *
-     * @param invoke The <code>Invoke</code>.
-     * @return The Invoker.
+     * @return Returns the next event from the internal event queue, if available
      */
-    public Invoker getInvoker(final Invoke invoke) {
-        return invokers.get(invokeIds.get(invoke));
+    public TriggerEvent nextInternalEvent() {
+        return internalEventQueue.poll();
     }
 
     /**
@@ -485,6 +420,16 @@ public class SCXMLExecutionContext implements SCXMLIOProcessor {
         initializeIOProcessors();
     }
 
+    /**
+     * Register an Invoker for this target type.
+     *
+     * @param type The target type (specified by "type" attribute of the invoke element).
+     * @param invokerClass The Invoker class.
+     */
+    protected void registerInvokerClass(final String type, final Class<? extends Invoker> invokerClass) {
+        invokerClasses.put(stripTrailingSlash(type), invokerClass);
+    }
+
     /**
      * Remove a previously active Invoker, which must already have been canceled
      * @param invoke The Invoke for the Invoker to remove
@@ -496,51 +441,106 @@ public class SCXMLExecutionContext implements SCXMLIOProcessor {
     }
 
     /**
-     * @return Returns the map of current active Invokes and their invokeId
+     * Sets if the SCXML configuration should be checked before execution (default = true)
+     * @param checkLegalConfiguration flag to set
      */
-    public Map<Invoke, String> getInvokeIds() {
-        return invokeIds;
+    public void setCheckLegalConfiguration(final boolean checkLegalConfiguration) {
+        this.checkLegalConfiguration = checkLegalConfiguration;
     }
 
+    /**
+     * Sets or replace the error reporter
+     *
+     * @param errorReporter The error reporter to set, if null a SimpleErrorReporter instance will be used instead
+     */
+    protected void setErrorReporter(final ErrorReporter errorReporter) {
+        this.errorReporter = errorReporter != null ? errorReporter : new SimpleErrorReporter();
+        try {
+            scInstance.setErrorReporter(errorReporter);
+        }
+        catch (final ModelException me) {
+            // won't happen
+        }
+    }
 
     /**
-     * Cancel and remove an active Invoker
+     * Sets or replace the evaluator
+     * <p>
+     * If the state machine instance has been initialized before, it will be initialized again, destroying all existing
+     * state!
+     * </p>
+     * @param evaluator The evaluator to set
+     * @throws ModelException if attempting to set a null value or the state machine instance failed to re-initialize
+     */
+    protected void setEvaluator(final Evaluator evaluator) throws ModelException {
+        scInstance.setEvaluator(evaluator, false);
+        // synchronize possible derived evaluator
+        this.evaluator = scInstance.getEvaluator();
+        initializeIOProcessors();
+    }
+
+    /**
+     * Sets or replace the event dispatch
      *
-     * @param invoke The Invoke for the Invoker to cancel
+     * @param eventdispatcher The event dispatcher to set, if null a SimpleDispatcher instance will be used instead
      */
-    public void cancelInvoker(final Invoke invoke) {
-        final String invokeId = invokeIds.get(invoke);
-        if (invokeId != null) {
-            try {
-                invokers.get(invokeId).cancel();
-            } catch (final InvokerException ie) {
-                final TriggerEvent te = new EventBuilder("failed.invoke.cancel."+invokeId, TriggerEvent.ERROR_EVENT).build();
-                addEvent(te);
-            }
-            removeInvoker(invoke);
+    protected void setEventdispatcher(final EventDispatcher eventdispatcher) {
+        this.eventdispatcher = eventdispatcher != null ? eventdispatcher : new SimpleDispatcher();
+    }
+
+    /**
+     * Sets or replace the state machine to be executed
+     * <p>
+     * If the state machine instance has been initialized before, it will be initialized again, destroying all existing
+     * state!
+     * </p>
+     * @param stateMachine The state machine to set
+     * @throws ModelException if attempting to set a null value or the state machine instance failed to re-initialize
+     */
+    protected void setStateMachine(final SCXML stateMachine) throws ModelException {
+        scInstance.setStateMachine(stateMachine);
+        // synchronize possible derived evaluator
+        this.evaluator = scInstance.getEvaluator();
+        initializeIOProcessors();
+    }
+
+
+    /**
+     * (re)start the state machine.
+     */
+    public void start() {
+        if (scInstance.isRunning()) {
+            throw new IllegalStateException("The state machine has already started.");
+        }
+        if (scInstance.getGlobalContext() == null) {
+            throw new IllegalStateException("The state machine has not been initialized yet.");
         }
+        scInstance.start();
     }
 
     /**
-     * Add an event to the internal event queue
-     * @param event The event
+     * Stop the state machine
      */
-    @Override
-    public void addEvent(final TriggerEvent event) {
-        internalEventQueue.add(event);
+    public void stop() {
+        scInstance.stop();
     }
 
     /**
-     * @return Returns the next event from the internal event queue, if available
+     * Trivial utility method needed for SCXML IRP test 216 which (IMO incorrectly uses http://www.w3.org/TR/scxml
+     * (no trailing /) while the SCXML spec explicitly states http://www.w3.org/TR/scxml/ should be used (supported)
+     * @param uri
+     * @return
      */
-    public TriggerEvent nextInternalEvent() {
-        return internalEventQueue.poll();
+    private String stripTrailingSlash(final String uri) {
+        return uri.endsWith("/") ? uri.substring(0, uri.length()-1) : uri;
     }
 
     /**
-     * @return Returns true if the internal event queue isn't empty
+     * Remove the Invoker registered for this target type (if there is one registered).
+     *
+     * @param type The target type (specified by "type" attribute of the invoke element).
      */
-    public boolean hasPendingInternalEvent() {
-        return !internalEventQueue.isEmpty();
+    protected void unregisterInvokerClass(final String type) {
+        invokerClasses.remove(stripTrailingSlash(type));
     }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/SCXMLExecutor.java b/src/main/java/org/apache/commons/scxml2/SCXMLExecutor.java
index 43d38344..cfe0fc30 100644
--- a/src/main/java/org/apache/commons/scxml2/SCXMLExecutor.java
+++ b/src/main/java/org/apache/commons/scxml2/SCXMLExecutor.java
@@ -125,78 +125,68 @@ public class SCXMLExecutor implements SCXMLIOProcessor {
     }
 
     /**
-     * @return the parent SCXMLIOProcessor (if any)
+     * Add a new external event, which may be done concurrently, and even when the current SCInstance is detached.
+     * <p>
+     * No processing of the vent will be done, until the next triggerEvent methods is invoked.
+     * </p>
+     * @param evt an external event
      */
-    public ParentSCXMLIOProcessor getParentSCXMLIOProcessor() {
-        return parentSCXMLIOProcessor;
+    @Override
+    public void addEvent(final TriggerEvent evt) {
+        if (evt != null) {
+            externalEventQueue.add(evt);
+        }
     }
 
     /**
-     * Gets the current state machine instance status.
+     * Add a listener to the {@link Observable}.
      *
-     * @return The current Status
+     * @param observable The {@link Observable} to attach the listener to.
+     * @param listener The SCXMLListener.
      */
-    public synchronized Status getStatus() {
-        return exctx.getScInstance().getCurrentStatus();
+    public void addListener(final Observable observable, final SCXMLListener listener) {
+        exctx.getNotificationRegistry().addListener(observable, listener);
     }
 
     /**
-     * @return the (optionally) &lt;final&gt;&lt;donedata/&gt;&lt;/final&gt; produced data after the current statemachine
-     *         completed its execution.
+     * Re-attach a previously detached SCInstance.
+     * <p>
+     * Note: an already attached instance will get overwritten (and thus lost).
+     * </p>
+     * @param instance An previously detached SCInstance
      */
-    public Object getFinalDoneData() {
-        return getGlobalContext().getSystemContext().getPlatformVariables().get(SCXMLSystemContext.FINAL_DONE_DATA_KEY);
+    public void attachInstance(final SCInstance instance) {
+        exctx.attachInstance(instance);
     }
 
     /**
-     * starts the state machine with a specific active configuration, as the result of a (first) step
+     * Detach the current SCInstance to allow external serialization.
      * <p>
-     * This will first (re)initialize the current state machine: clearing all variable contexts, histories and current
-     * status, and clones the SCXML root datamodel into the root context.
+     * {@link #attachInstance(SCInstance)} can be used to re-attach a previously detached instance
      * </p>
-     * @param atomicStateIds The set of atomic state ids for the state machine
-     * @throws ModelException when the state machine hasn't been properly configured yet, when an unknown or illegal
-     * stateId is specified, or when the specified active configuration does not represent a legal configuration.
-     * @see SCInstance#initialize()
-     * @see SCXMLSemantics#isLegalConfiguration(java.util.Set, ErrorReporter)
+     * <p>
+     * Note: until an instance is re-attached, no operations are allowed (and probably throw exceptions) except
+     * for {@link #addEvent(TriggerEvent)} which might still be used (concurrently) by running Invokers, or
+     * {@link #hasPendingEvents()} to check for possible pending events.
+     * </p>
+     * @return the detached instance
      */
-    public synchronized void setConfiguration(final Set<String> atomicStateIds) throws ModelException {
-        semantics.initialize(exctx, Collections.emptyMap());
-        final Set<EnterableState> states = new HashSet<>();
-        for (final String stateId : atomicStateIds) {
-            final TransitionTarget tt = getStateMachine().getTargets().get(stateId);
-            if (!(tt instanceof EnterableState) || !((EnterableState)tt).isAtomicState()) {
-                throw new ModelException("Illegal atomic stateId "+stateId+": state unknown or not an atomic state");
-            }
-            EnterableState es = (EnterableState)tt;
-            while (es != null && !states.add(es)) {
-                es = es.getParent();
-            }
-        }
-        if (!semantics.isLegalConfiguration(states, getErrorReporter())) {
-            throw new ModelException("Illegal state machine configuration!");
-        }
-        for (final EnterableState es : states) {
-            exctx.getScInstance().getStateConfiguration().enterState(es);
-        }
+    public SCInstance detachInstance() {
+        return exctx.detachInstance();
+    }
+
+    protected void eventStep(final TriggerEvent event) throws ModelException {
+        semantics.nextStep(exctx, event);
         logState();
-        exctx.start();
     }
 
     /**
-     * Sets or replace the expression evaluator
-     * <p>
-     * If the state machine instance has been initialized before, it will be initialized again, destroying all existing
-     * state!
-     * </p>
-     * <p>
-     * Also the external event queue will be cleared.
-     * </p>
-     * @param evaluator The evaluator to set
-     * @throws ModelException if attempting to set a null value or the state machine instance failed to re-initialize
+     * Gets the environment specific error reporter.
+     *
+     * @return Returns the errorReporter.
      */
-    public void setEvaluator(final Evaluator evaluator) throws ModelException {
-        exctx.setEvaluator(evaluator);
+    public ErrorReporter getErrorReporter() {
+        return exctx.getErrorReporter();
     }
 
     /**
@@ -209,15 +199,20 @@ public class SCXMLExecutor implements SCXMLIOProcessor {
     }
 
     /**
-     * Gets the root context for the state machine execution.
-     * <p>
-     * The root context can be used for providing external data to the state machine
-     * </p>
+     * Gets the event dispatcher.
      *
-     * @return Context The root context.
+     * @return Returns the eventdispatcher.
      */
-    public Context getRootContext() {
-        return exctx.getScInstance().getRootContext();
+    public EventDispatcher getEventdispatcher() {
+        return exctx.getEventDispatcher();
+    }
+
+    /**
+     * @return the (optionally) &lt;final&gt;&lt;donedata/&gt;&lt;/final&gt; produced data after the current statemachine
+     *         completed its execution.
+     */
+    public Object getFinalDoneData() {
+        return getGlobalContext().getSystemContext().getPlatformVariables().get(SCXMLSystemContext.FINAL_DONE_DATA_KEY);
     }
 
     /**
@@ -233,21 +228,47 @@ public class SCXMLExecutor implements SCXMLIOProcessor {
     }
 
     /**
-     * Sets the root context for the state machine execution.
-     * <b>NOTE:</b> Should only be used before the executor is set in motion.
+     * Gets the notification registry.
      *
-     * @param rootContext The Context that ties to the host environment.
+     * @return The notification registry.
      */
-    public void setRootContext(final Context rootContext) {
-        exctx.getScInstance().setRootContext(rootContext);
+    public NotificationRegistry getNotificationRegistry() {
+        return exctx.getNotificationRegistry();
     }
 
-    public void setSingleContext(final boolean singleContext) throws ModelException {
-        getSCInstance().setSingleContext(singleContext);
+    /**
+     * @return the parent SCXMLIOProcessor (if any)
+     */
+    public ParentSCXMLIOProcessor getParentSCXMLIOProcessor() {
+        return parentSCXMLIOProcessor;
     }
 
-    public boolean isSingleContext() {
-        return getSCInstance().isSingleContext();
+    /**
+     * @return Returns the current number of pending external events to be processed.
+     */
+    public int getPendingEvents() {
+        return externalEventQueue.size();
+    }
+
+    /**
+     * Gets the root context for the state machine execution.
+     * <p>
+     * The root context can be used for providing external data to the state machine
+     * </p>
+     *
+     * @return Context The root context.
+     */
+    public Context getRootContext() {
+        return exctx.getScInstance().getRootContext();
+    }
+
+    /**
+     * Gets the state chart instance for this executor.
+     *
+     * @return The SCInstance for this executor.
+     */
+    protected SCInstance getSCInstance() {
+        return exctx.getScInstance();
     }
 
     /**
@@ -267,64 +288,42 @@ public class SCXMLExecutor implements SCXMLIOProcessor {
     }
 
     /**
-     * Sets or replace the state machine to be executed
-     * <p>
-     * If the state machine instance has been initialized before, it will be initialized again, destroying all existing
-     * state!
-     * </p>
-     * <p>
-     * Also the external event queue will be cleared.
-     * </p>
-     * @param stateMachine The state machine to set
-     * @throws ModelException if attempting to set a null value or the state machine instance failed to re-initialize
-     */
-    public void setStateMachine(final SCXML stateMachine) throws ModelException {
-        exctx.setStateMachine(semantics.normalizeStateMachine(stateMachine, exctx.getErrorReporter()));
-        externalEventQueue.clear();
-    }
-
-    /**
-     * Gets the environment specific error reporter.
+     * Gets the current state machine instance status.
      *
-     * @return Returns the errorReporter.
+     * @return The current Status
      */
-    public ErrorReporter getErrorReporter() {
-        return exctx.getErrorReporter();
+    public synchronized Status getStatus() {
+        return exctx.getScInstance().getCurrentStatus();
     }
 
-    /**
-     * Sets or replace the error reporter
-     *
-     * @param errorReporter The error reporter to set, if null a SimpleErrorReporter instance will be used instead
-     */
-    public void setErrorReporter(final ErrorReporter errorReporter) {
-        exctx.setErrorReporter(errorReporter);
+    public void go() throws ModelException {
+        go(Collections.emptyMap());
     }
 
     /**
-     * Gets the event dispatcher.
-     *
-     * @return Returns the eventdispatcher.
+     * Clear all state, optionally initialize/override global context data, and begin executing the state machine
+     * @param data optional data to initialize/override data defined (only) in the global context of the state machine
+     * @throws ModelException if the state machine instance failed to initialize
      */
-    public EventDispatcher getEventdispatcher() {
-        return exctx.getEventDispatcher();
-    }
+    public void go(final Map<String, Object> data) throws ModelException {
+        // first stop the state machine (flag only, otherwise start may fail hereafter)
+        exctx.stop();
+        // clear any pending external events
+        externalEventQueue.clear();
 
-    /**
-     * Sets or replace the event dispatch
-     *
-     * @param eventdispatcher The event dispatcher to set, if null a SimpleDispatcher instance will be used instead
-     */
-    public void setEventdispatcher(final EventDispatcher eventdispatcher) {
-        exctx.setEventdispatcher(eventdispatcher);
+        // (re)initialize
+        semantics.initialize(exctx, data);
+
+        // begin
+        semantics.firstStep(exctx);
+        logState();
     }
 
     /**
-     * Sets if the SCXML configuration should be checked before execution (default = true)
-     * @param checkLegalConfiguration flag to set
+     * @return Returns true if there are pending external events to be processed.
      */
-    public void setCheckLegalConfiguration(final boolean checkLegalConfiguration) {
-        this.exctx.setCheckLegalConfiguration(checkLegalConfiguration);
+    public boolean hasPendingEvents() {
+        return !externalEventQueue.isEmpty();
     }
 
     /**
@@ -335,33 +334,29 @@ public class SCXMLExecutor implements SCXMLIOProcessor {
     }
 
     /**
-     * Gets the notification registry.
-     *
-     * @return The notification registry.
+     * @return Returns true if the state machine is running
      */
-    public NotificationRegistry getNotificationRegistry() {
-        return exctx.getNotificationRegistry();
+    public boolean isRunning() {
+        return exctx.isRunning();
     }
 
-    /**
-     * Add a listener to the {@link Observable}.
-     *
-     * @param observable The {@link Observable} to attach the listener to.
-     * @param listener The SCXMLListener.
-     */
-    public void addListener(final Observable observable, final SCXMLListener listener) {
-        exctx.getNotificationRegistry().addListener(observable, listener);
+    public boolean isSingleContext() {
+        return getSCInstance().isSingleContext();
     }
 
     /**
-     * Remove this listener from the {@link Observable}.
-     *
-     * @param observable The {@link Observable}.
-     * @param listener The SCXMLListener to be removed.
+     * Log the current set of active states.
      */
-    public void removeListener(final Observable observable,
-                               final SCXMLListener listener) {
-        exctx.getNotificationRegistry().removeListener(observable, listener);
+    protected void logState() {
+        if (log.isDebugEnabled()) {
+            final StringBuilder sb = new StringBuilder("Current States: [ ");
+            for (final EnterableState es : getStatus().getStates()) {
+                sb.append(es.getId()).append(", ");
+            }
+            final int length = sb.length();
+            sb.delete(length - 2, length).append(" ]");
+            log.debug(sb.toString());
+        }
     }
 
     /**
@@ -375,69 +370,14 @@ public class SCXMLExecutor implements SCXMLIOProcessor {
     }
 
     /**
-     * Remove the Invoker registered for this target type (if there is one registered).
+     * Remove this listener from the {@link Observable}.
      *
-     * @param type The target type (specified by "type" attribute of the invoke element).
-     */
-    public void unregisterInvokerClass(final String type) {
-        exctx.unregisterInvokerClass(type);
-    }
-
-    /**
-     * Detach the current SCInstance to allow external serialization.
-     * <p>
-     * {@link #attachInstance(SCInstance)} can be used to re-attach a previously detached instance
-     * </p>
-     * <p>
-     * Note: until an instance is re-attached, no operations are allowed (and probably throw exceptions) except
-     * for {@link #addEvent(TriggerEvent)} which might still be used (concurrently) by running Invokers, or
-     * {@link #hasPendingEvents()} to check for possible pending events.
-     * </p>
-     * @return the detached instance
-     */
-    public SCInstance detachInstance() {
-        return exctx.detachInstance();
-    }
-
-    /**
-     * Re-attach a previously detached SCInstance.
-     * <p>
-     * Note: an already attached instance will get overwritten (and thus lost).
-     * </p>
-     * @param instance An previously detached SCInstance
-     */
-    public void attachInstance(final SCInstance instance) {
-        exctx.attachInstance(instance);
-    }
-
-    /**
-     * @return Returns true if the state machine is running
-     */
-    public boolean isRunning() {
-        return exctx.isRunning();
-    }
-
-    public void go() throws ModelException {
-        go(Collections.emptyMap());
-    }
-
-    /**
-     * Clear all state, optionally initialize/override global context data, and begin executing the state machine
-     * @param data optional data to initialize/override data defined (only) in the global context of the state machine
-     * @throws ModelException if the state machine instance failed to initialize
+     * @param observable The {@link Observable}.
+     * @param listener The SCXMLListener to be removed.
      */
-    public void go(final Map<String, Object> data) throws ModelException {
-        // first stop the state machine (flag only, otherwise start may fail hereafter)
-        exctx.stop();
-        // clear any pending external events
-        externalEventQueue.clear();
-
-        // (re)initialize
-        semantics.initialize(exctx, data);
-
-        // begin
-        semantics.firstStep(exctx);
-        logState();
+    public void removeListener(final Observable observable,
+                               final SCXMLListener listener) {
+        exctx.getNotificationRegistry().removeListener(observable, listener);
     }
 
     /**
@@ -467,31 +407,111 @@ public class SCXMLExecutor implements SCXMLIOProcessor {
     }
 
     /**
-     * Add a new external event, which may be done concurrently, and even when the current SCInstance is detached.
+     * Sets if the SCXML configuration should be checked before execution (default = true)
+     * @param checkLegalConfiguration flag to set
+     */
+    public void setCheckLegalConfiguration(final boolean checkLegalConfiguration) {
+        this.exctx.setCheckLegalConfiguration(checkLegalConfiguration);
+    }
+
+    /**
+     * starts the state machine with a specific active configuration, as the result of a (first) step
      * <p>
-     * No processing of the vent will be done, until the next triggerEvent methods is invoked.
+     * This will first (re)initialize the current state machine: clearing all variable contexts, histories and current
+     * status, and clones the SCXML root datamodel into the root context.
      * </p>
-     * @param evt an external event
+     * @param atomicStateIds The set of atomic state ids for the state machine
+     * @throws ModelException when the state machine hasn't been properly configured yet, when an unknown or illegal
+     * stateId is specified, or when the specified active configuration does not represent a legal configuration.
+     * @see SCInstance#initialize()
+     * @see SCXMLSemantics#isLegalConfiguration(java.util.Set, ErrorReporter)
      */
-    @Override
-    public void addEvent(final TriggerEvent evt) {
-        if (evt != null) {
-            externalEventQueue.add(evt);
+    public synchronized void setConfiguration(final Set<String> atomicStateIds) throws ModelException {
+        semantics.initialize(exctx, Collections.emptyMap());
+        final Set<EnterableState> states = new HashSet<>();
+        for (final String stateId : atomicStateIds) {
+            final TransitionTarget tt = getStateMachine().getTargets().get(stateId);
+            if (!(tt instanceof EnterableState) || !((EnterableState)tt).isAtomicState()) {
+                throw new ModelException("Illegal atomic stateId "+stateId+": state unknown or not an atomic state");
+            }
+            EnterableState es = (EnterableState)tt;
+            while (es != null && !states.add(es)) {
+                es = es.getParent();
+            }
         }
+        if (!semantics.isLegalConfiguration(states, getErrorReporter())) {
+            throw new ModelException("Illegal state machine configuration!");
+        }
+        for (final EnterableState es : states) {
+            exctx.getScInstance().getStateConfiguration().enterState(es);
+        }
+        logState();
+        exctx.start();
     }
 
     /**
-     * @return Returns true if there are pending external events to be processed.
+     * Sets or replace the error reporter
+     *
+     * @param errorReporter The error reporter to set, if null a SimpleErrorReporter instance will be used instead
      */
-    public boolean hasPendingEvents() {
-        return !externalEventQueue.isEmpty();
+    public void setErrorReporter(final ErrorReporter errorReporter) {
+        exctx.setErrorReporter(errorReporter);
     }
 
     /**
-     * @return Returns the current number of pending external events to be processed.
+     * Sets or replace the expression evaluator
+     * <p>
+     * If the state machine instance has been initialized before, it will be initialized again, destroying all existing
+     * state!
+     * </p>
+     * <p>
+     * Also the external event queue will be cleared.
+     * </p>
+     * @param evaluator The evaluator to set
+     * @throws ModelException if attempting to set a null value or the state machine instance failed to re-initialize
      */
-    public int getPendingEvents() {
-        return externalEventQueue.size();
+    public void setEvaluator(final Evaluator evaluator) throws ModelException {
+        exctx.setEvaluator(evaluator);
+    }
+
+    /**
+     * Sets or replace the event dispatch
+     *
+     * @param eventdispatcher The event dispatcher to set, if null a SimpleDispatcher instance will be used instead
+     */
+    public void setEventdispatcher(final EventDispatcher eventdispatcher) {
+        exctx.setEventdispatcher(eventdispatcher);
+    }
+
+    /**
+     * Sets the root context for the state machine execution.
+     * <b>NOTE:</b> Should only be used before the executor is set in motion.
+     *
+     * @param rootContext The Context that ties to the host environment.
+     */
+    public void setRootContext(final Context rootContext) {
+        exctx.getScInstance().setRootContext(rootContext);
+    }
+
+    public void setSingleContext(final boolean singleContext) throws ModelException {
+        getSCInstance().setSingleContext(singleContext);
+    }
+
+    /**
+     * Sets or replace the state machine to be executed
+     * <p>
+     * If the state machine instance has been initialized before, it will be initialized again, destroying all existing
+     * state!
+     * </p>
+     * <p>
+     * Also the external event queue will be cleared.
+     * </p>
+     * @param stateMachine The state machine to set
+     * @throws ModelException if attempting to set a null value or the state machine instance failed to re-initialize
+     */
+    public void setStateMachine(final SCXML stateMachine) throws ModelException {
+        exctx.setStateMachine(semantics.normalizeStateMachine(stateMachine, exctx.getErrorReporter()));
+        externalEventQueue.clear();
     }
 
     /**
@@ -509,6 +529,17 @@ public class SCXMLExecutor implements SCXMLIOProcessor {
         triggerEvents();
     }
 
+    /**
+     * Trigger all pending and incoming events, until there are no more pending events
+     * @throws ModelException in case there is a fatal SCXML object model problem.
+     */
+    public void triggerEvents() throws ModelException {
+        TriggerEvent evt;
+        while (exctx.isRunning() && (evt = externalEventQueue.poll()) != null) {
+            eventStep(evt);
+        }
+    }
+
     /**
      * The worker method.
      * Re-evaluates current status whenever any events are triggered.
@@ -530,42 +561,11 @@ public class SCXMLExecutor implements SCXMLIOProcessor {
     }
 
     /**
-     * Trigger all pending and incoming events, until there are no more pending events
-     * @throws ModelException in case there is a fatal SCXML object model problem.
-     */
-    public void triggerEvents() throws ModelException {
-        TriggerEvent evt;
-        while (exctx.isRunning() && (evt = externalEventQueue.poll()) != null) {
-            eventStep(evt);
-        }
-    }
-
-    protected void eventStep(final TriggerEvent event) throws ModelException {
-        semantics.nextStep(exctx, event);
-        logState();
-    }
-
-    /**
-     * Gets the state chart instance for this executor.
+     * Remove the Invoker registered for this target type (if there is one registered).
      *
-     * @return The SCInstance for this executor.
-     */
-    protected SCInstance getSCInstance() {
-        return exctx.getScInstance();
-    }
-
-    /**
-     * Log the current set of active states.
+     * @param type The target type (specified by "type" attribute of the invoke element).
      */
-    protected void logState() {
-        if (log.isDebugEnabled()) {
-            final StringBuilder sb = new StringBuilder("Current States: [ ");
-            for (final EnterableState es : getStatus().getStates()) {
-                sb.append(es.getId()).append(", ");
-            }
-            final int length = sb.length();
-            sb.delete(length - 2, length).append(" ]");
-            log.debug(sb.toString());
-        }
+    public void unregisterInvokerClass(final String type) {
+        exctx.unregisterInvokerClass(type);
     }
 }
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/scxml2/SCXMLExpressionException.java b/src/main/java/org/apache/commons/scxml2/SCXMLExpressionException.java
index 15821956..b0d193aa 100644
--- a/src/main/java/org/apache/commons/scxml2/SCXMLExpressionException.java
+++ b/src/main/java/org/apache/commons/scxml2/SCXMLExpressionException.java
@@ -41,14 +41,6 @@ public class SCXMLExpressionException extends Exception {
         super(message);
     }
 
-    /**
-     * @see Exception#Exception(Throwable)
-     * @param cause The cause
-     */
-    public SCXMLExpressionException(final Throwable cause) {
-        super(cause);
-    }
-
     /**
      * @see Exception#Exception(String, Throwable)
      * @param message The error message
@@ -59,5 +51,13 @@ public class SCXMLExpressionException extends Exception {
         super(message, cause);
     }
 
+    /**
+     * @see Exception#Exception(Throwable)
+     * @param cause The cause
+     */
+    public SCXMLExpressionException(final Throwable cause) {
+        super(cause);
+    }
+
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/SCXMLSemantics.java b/src/main/java/org/apache/commons/scxml2/SCXMLSemantics.java
index 72210949..0ee98d67 100644
--- a/src/main/java/org/apache/commons/scxml2/SCXMLSemantics.java
+++ b/src/main/java/org/apache/commons/scxml2/SCXMLSemantics.java
@@ -71,24 +71,26 @@ import org.apache.commons.scxml2.model.SCXML;
 public interface SCXMLSemantics {
 
     /**
-     * Optional post processing after loading an {@link SCXML} document, invoked by {@link SCXMLExecutor}
-     * when setting the {@link SCXMLExecutor#setStateMachine(SCXML)}. May be used for removing pseudo-states etc.
-     *
-     * @param input  SCXML state machine
-     * @param errRep ErrorReporter callback
-     * @return normalized SCXML state machine, pseudo states are removed, etc.
-     */
-    SCXML normalizeStateMachine(final SCXML input, final ErrorReporter errRep);
-
-    /**
-     * Initialize the SCXML state machine, optionally initializing (overriding) root &lt;datamodel&gt;&lt;data&gt; elements
-     * with external values provided through a data map.
-     * @param data A data map to initialize/override &lt;data&gt; elements in the root (global) &lt;datamodel&gt; with
-     *             ids matching the keys in the map (other data map entries will be ignored)
-     * @param exctx The execution context to initialize
-     * @throws ModelException if the state machine hasn't been setup for the internal SCXML instance
+     * The final step in the execution of an SCXML state machine.
+     * <p>
+     * This final step is corresponding to the Algorithm for SCXML processing exitInterpreter() procedure, after the
+     * state machine stopped running.
+     * </p>
+     * <p>
+     * If the state machine still is {@link SCXMLExecutionContext#isRunning()} invoking this method should simply
+     * do nothing.
+     * </p>
+     * <p>
+     * This final step should first exit all remaining active states and cancel any active invokers, before handling
+     * the possible donedata element for the last final state.
+     * </p>
+     * <p>
+     *  <em>NOTE: the current implementation does not yet provide final donedata handling.</em>
+     * </p>
+     * @param exctx The execution context for this step
+     * @throws ModelException if a SCXML model error occurred during the execution.
      */
-    void initialize(final SCXMLExecutionContext exctx, final Map<String, Object> data) throws ModelException;
+    void finalStep(final SCXMLExecutionContext exctx) throws ModelException;
 
     /**
      * First step in the execution of an SCXML state machine.
@@ -114,6 +116,35 @@ public interface SCXMLSemantics {
      */
     void firstStep(final SCXMLExecutionContext exctx) throws ModelException;
 
+    /**
+     * Initialize the SCXML state machine, optionally initializing (overriding) root &lt;datamodel&gt;&lt;data&gt; elements
+     * with external values provided through a data map.
+     * @param data A data map to initialize/override &lt;data&gt; elements in the root (global) &lt;datamodel&gt; with
+     *             ids matching the keys in the map (other data map entries will be ignored)
+     * @param exctx The execution context to initialize
+     * @throws ModelException if the state machine hasn't been setup for the internal SCXML instance
+     */
+    void initialize(final SCXMLExecutionContext exctx, final Map<String, Object> data) throws ModelException;
+
+    /**
+     * Checks whether a given set of states is a legal Harel State Table
+     * configuration (with the respect to the definition of the OR and AND
+     * states).
+     * <p>
+     * When {@link SCXMLExecutionContext#isCheckLegalConfiguration()} is true (default) the SCXMLSemantics implementation
+     * <em>should</em> invoke this method before executing a step, and throw a ModelException if a non-legal
+     * configuration is encountered.
+     * </p>
+     * <p>
+     * This method is also first invoked when manually initializing the status of a state machine through
+     * {@link SCXMLExecutor#setConfiguration(java.util.Set)}.
+     * </p>
+     * @param states a set of states
+     * @param errRep ErrorReporter to report detailed error info if needed
+     * @return true if a given state configuration is legal, false otherwise
+     */
+    boolean isLegalConfiguration(final Set<EnterableState> states, final ErrorReporter errRep);
+
     /**
      * Next step in the execution of an SCXML state machine.
      * <p>
@@ -142,43 +173,12 @@ public interface SCXMLSemantics {
     void nextStep(final SCXMLExecutionContext exctx, final TriggerEvent event) throws ModelException;
 
     /**
-     * The final step in the execution of an SCXML state machine.
-     * <p>
-     * This final step is corresponding to the Algorithm for SCXML processing exitInterpreter() procedure, after the
-     * state machine stopped running.
-     * </p>
-     * <p>
-     * If the state machine still is {@link SCXMLExecutionContext#isRunning()} invoking this method should simply
-     * do nothing.
-     * </p>
-     * <p>
-     * This final step should first exit all remaining active states and cancel any active invokers, before handling
-     * the possible donedata element for the last final state.
-     * </p>
-     * <p>
-     *  <em>NOTE: the current implementation does not yet provide final donedata handling.</em>
-     * </p>
-     * @param exctx The execution context for this step
-     * @throws ModelException if a SCXML model error occurred during the execution.
-     */
-    void finalStep(final SCXMLExecutionContext exctx) throws ModelException;
-
-    /**
-     * Checks whether a given set of states is a legal Harel State Table
-     * configuration (with the respect to the definition of the OR and AND
-     * states).
-     * <p>
-     * When {@link SCXMLExecutionContext#isCheckLegalConfiguration()} is true (default) the SCXMLSemantics implementation
-     * <em>should</em> invoke this method before executing a step, and throw a ModelException if a non-legal
-     * configuration is encountered.
-     * </p>
-     * <p>
-     * This method is also first invoked when manually initializing the status of a state machine through
-     * {@link SCXMLExecutor#setConfiguration(java.util.Set)}.
-     * </p>
-     * @param states a set of states
-     * @param errRep ErrorReporter to report detailed error info if needed
-     * @return true if a given state configuration is legal, false otherwise
+     * Optional post processing after loading an {@link SCXML} document, invoked by {@link SCXMLExecutor}
+     * when setting the {@link SCXMLExecutor#setStateMachine(SCXML)}. May be used for removing pseudo-states etc.
+     *
+     * @param input  SCXML state machine
+     * @param errRep ErrorReporter callback
+     * @return normalized SCXML state machine, pseudo states are removed, etc.
      */
-    boolean isLegalConfiguration(final Set<EnterableState> states, final ErrorReporter errRep);
+    SCXML normalizeStateMachine(final SCXML input, final ErrorReporter errRep);
 }
diff --git a/src/main/java/org/apache/commons/scxml2/SCXMLSystemContext.java b/src/main/java/org/apache/commons/scxml2/SCXMLSystemContext.java
index 806c1c35..a48974de 100644
--- a/src/main/java/org/apache/commons/scxml2/SCXMLSystemContext.java
+++ b/src/main/java/org/apache/commons/scxml2/SCXMLSystemContext.java
@@ -74,24 +74,6 @@ public class SCXMLSystemContext implements Context, Serializable {
      */
     private long nextSessionSequenceId;
 
-    /**
-     * Initialize or replace systemContext
-     * @param systemContext the system context to set
-     * @throws NullPointerException if systemContext == null
-     */
-    void setSystemContext(final Context systemContext) {
-        if (this.systemContext != null) {
-            // replace systemContext
-            systemContext.getVars().putAll(this.systemContext.getVars());
-        }
-        else {
-            // create Platform variables map
-            systemContext.setLocal(X_KEY, new HashMap<String, Object>());
-        }
-        this.systemContext = systemContext;
-        this.protectedVars = Collections.unmodifiableMap(systemContext.getVars());
-    }
-
     /**
      * The unmodifiable wrapped variables map from the wrapped system context
      */
@@ -106,22 +88,38 @@ public class SCXMLSystemContext implements Context, Serializable {
     }
 
     @Override
-    public void set(final String name, final Object value) {
-        if (PROTECTED_NAMES.contains(name)) {
-            throw new UnsupportedOperationException();
-        }
-        // non-protected variables are set on the parent of the system context (e.g. root context)
-        systemContext.getParent().set(name, value);
+    public Object get(final String name) {
+        return systemContext.get(name);
+    }
+
+    /**
+     * @return Returns the wrapped (modifiable) system context
+     */
+    Context getContext() {
+        return systemContext;
     }
 
     @Override
-    public void setLocal(final String name, final Object value) {
-        throw new UnsupportedOperationException();
+    public Context getParent() {
+        return systemContext.getParent();
+    }
+
+    /**
+     * @return The Platform specific system variables map stored under the {@link #X_KEY _x} root system variable
+     */
+    @SuppressWarnings("unchecked")
+    public Map<String, Object> getPlatformVariables() {
+        return (Map<String, Object>)get(X_KEY);
     }
 
     @Override
-    public Object get(final String name) {
-        return systemContext.get(name);
+    public SCXMLSystemContext getSystemContext() {
+        return this;
+    }
+
+    @Override
+    public Map<String, Object> getVars() {
+        return protectedVars;
     }
 
     @Override
@@ -134,38 +132,40 @@ public class SCXMLSystemContext implements Context, Serializable {
         return systemContext.hasLocal(name);
     }
 
-    @Override
-    public Map<String, Object> getVars() {
-        return protectedVars;
-    }
-
     @Override
     public void reset() {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public Context getParent() {
-        return systemContext.getParent();
+    public void set(final String name, final Object value) {
+        if (PROTECTED_NAMES.contains(name)) {
+            throw new UnsupportedOperationException();
+        }
+        // non-protected variables are set on the parent of the system context (e.g. root context)
+        systemContext.getParent().set(name, value);
     }
 
     @Override
-    public SCXMLSystemContext getSystemContext() {
-        return this;
-    }
-
-    /**
-     * @return The Platform specific system variables map stored under the {@link #X_KEY _x} root system variable
-     */
-    @SuppressWarnings("unchecked")
-    public Map<String, Object> getPlatformVariables() {
-        return (Map<String, Object>)get(X_KEY);
+    public void setLocal(final String name, final Object value) {
+        throw new UnsupportedOperationException();
     }
 
     /**
-     * @return Returns the wrapped (modifiable) system context
+     * Initialize or replace systemContext
+     * @param systemContext the system context to set
+     * @throws NullPointerException if systemContext == null
      */
-    Context getContext() {
-        return systemContext;
+    void setSystemContext(final Context systemContext) {
+        if (this.systemContext != null) {
+            // replace systemContext
+            systemContext.getVars().putAll(this.systemContext.getVars());
+        }
+        else {
+            // create Platform variables map
+            systemContext.setLocal(X_KEY, new HashMap<String, Object>());
+        }
+        this.systemContext = systemContext;
+        this.protectedVars = Collections.unmodifiableMap(systemContext.getVars());
     }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/StateConfiguration.java b/src/main/java/org/apache/commons/scxml2/StateConfiguration.java
index c47fc704..6d7f7050 100644
--- a/src/main/java/org/apache/commons/scxml2/StateConfiguration.java
+++ b/src/main/java/org/apache/commons/scxml2/StateConfiguration.java
@@ -45,22 +45,11 @@ public class StateConfiguration implements Serializable {
     private final Set<EnterableState> atomicStatesSet = Collections.unmodifiableSet(atomicStates);
 
     /**
-     * Gets the active states
-     *
-     * @return active states including simple states and their
-     *         complex ancestors up to the root.
-     */
-    public Set<EnterableState> getActiveStates() {
-        return  activeStatesSet;
-    }
-
-    /**
-     * Gets the current atomic states (leaf only).
-     *
-     * @return Returns the atomic states - simple (leaf) states only.
+     * Clear the state configuration
      */
-    public Set<EnterableState> getStates() {
-        return  atomicStatesSet;
+    public void clear() {
+        activeStates.clear();
+        atomicStates.clear();
     }
 
     /**
@@ -92,10 +81,21 @@ public class StateConfiguration implements Serializable {
     }
 
     /**
-     * Clear the state configuration
+     * Gets the active states
+     *
+     * @return active states including simple states and their
+     *         complex ancestors up to the root.
      */
-    public void clear() {
-        activeStates.clear();
-        atomicStates.clear();
+    public Set<EnterableState> getActiveStates() {
+        return  activeStatesSet;
+    }
+
+    /**
+     * Gets the current atomic states (leaf only).
+     *
+     * @return Returns the atomic states - simple (leaf) states only.
+     */
+    public Set<EnterableState> getStates() {
+        return  atomicStatesSet;
     }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/Status.java b/src/main/java/org/apache/commons/scxml2/Status.java
index dddb15cc..b1d15adb 100644
--- a/src/main/java/org/apache/commons/scxml2/Status.java
+++ b/src/main/java/org/apache/commons/scxml2/Status.java
@@ -40,10 +40,13 @@ public class Status implements Serializable {
     }
 
     /**
-     * @return Whether the state machine terminated AND we reached a top level Final state.
+     * Gets the active states configuration.
+     *
+     * @return active states configuration including simple states and their
+     *         complex ancestors up to the root.
      */
-    public boolean isFinal() {
-        return getFinalState() != null;
+    public Set<EnterableState> getActiveStates() {
+        return configuration.getActiveStates();
     }
 
     /**
@@ -69,13 +72,10 @@ public class Status implements Serializable {
     }
 
     /**
-     * Gets the active states configuration.
-     *
-     * @return active states configuration including simple states and their
-     *         complex ancestors up to the root.
+     * @return Whether the state machine terminated AND we reached a top level Final state.
      */
-    public Set<EnterableState> getActiveStates() {
-        return configuration.getActiveStates();
+    public boolean isFinal() {
+        return getFinalState() != null;
     }
 
     public boolean isInState(final String state) {
diff --git a/src/main/java/org/apache/commons/scxml2/TriggerEvent.java b/src/main/java/org/apache/commons/scxml2/TriggerEvent.java
index 620a4aff..d9b111a0 100644
--- a/src/main/java/org/apache/commons/scxml2/TriggerEvent.java
+++ b/src/main/java/org/apache/commons/scxml2/TriggerEvent.java
@@ -31,28 +31,6 @@ public class TriggerEvent implements Serializable {
     /** Serial version UID. */
     private static final long serialVersionUID = 1L;
 
-    /**
-     * Constructor.
-     *
-     * @param name The event name
-     * @param type The event type
-     * @deprecated use {@link EventBuilder instead}
-     */
-    public TriggerEvent(final String name, final int type) {
-        this(name, type, null, null, null, null, null);
-    }
-
-    TriggerEvent(final String name, final int type, final String sendId, final String origin,
-                        final String originType, final String invokeId, final Object data) {
-        this.name = name != null ? name.trim() : "";
-        this.type = type;
-        this.sendId = sendId;
-        this.origin = origin;
-        this.originType = originType;
-        this.invokeId = invokeId;
-        this.data = data;
-    }
-
     /**
      * <code>CALL_EVENT</code>.
      */
@@ -120,23 +98,66 @@ public class TriggerEvent implements Serializable {
     public static final String ERROR_PLATFORM = "error.platform";
 
     private final String name;
+
     private final int type;
+
     private final String sendId;
     private final String origin;
     private final String originType;
     private final String invokeId;
     private final Object data;
+    /**
+     * Constructor.
+     *
+     * @param name The event name
+     * @param type The event type
+     * @deprecated use {@link EventBuilder instead}
+     */
+    public TriggerEvent(final String name, final int type) {
+        this(name, type, null, null, null, null, null);
+    }
+    TriggerEvent(final String name, final int type, final String sendId, final String origin,
+                        final String originType, final String invokeId, final Object data) {
+        this.name = name != null ? name.trim() : "";
+        this.type = type;
+        this.sendId = sendId;
+        this.origin = origin;
+        this.originType = originType;
+        this.invokeId = invokeId;
+        this.data = data;
+    }
 
-    public String getName() {
-        return name;
+    /**
+     * Define an equals operator for TriggerEvent.
+     *
+     * @see Object#equals(Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj instanceof TriggerEvent) {
+            final TriggerEvent te2 = (TriggerEvent) obj;
+            if (type == te2.type && name.equals(te2.name) &&
+                    Objects.equals(sendId, te2.sendId) &&
+                    Objects.equals(origin, te2.origin) &&
+                    Objects.equals(originType, te2.originType) &&
+                    Objects.equals(invokeId, te2.invokeId) &&
+                    Objects.equals(data, te2.data)) {
+                return true;
+            }
+        }
+        return false;
     }
 
-    public int getType() {
-        return type;
+    public Object getData() {
+        return data;
     }
 
-    public String getSendId() {
-        return sendId;
+    public String getInvokeId() {
+        return invokeId;
+    }
+
+    public String getName() {
+        return name;
     }
 
     public String getOrigin() {
@@ -147,33 +168,22 @@ public class TriggerEvent implements Serializable {
         return originType;
     }
 
-    public String getInvokeId() {
-        return invokeId;
+    public String getSendId() {
+        return sendId;
     }
 
-    public Object getData() {
-        return data;
+    public int getType() {
+        return type;
     }
 
     /**
-     * Define an equals operator for TriggerEvent.
+     * Returns the hash code for this TriggerEvent object.
      *
-     * @see Object#equals(Object)
+     * @see Object#hashCode()
      */
     @Override
-    public boolean equals(final Object obj) {
-        if (obj instanceof TriggerEvent) {
-            final TriggerEvent te2 = (TriggerEvent) obj;
-            if (type == te2.type && name.equals(te2.name) &&
-                    Objects.equals(sendId, te2.sendId) &&
-                    Objects.equals(origin, te2.origin) &&
-                    Objects.equals(originType, te2.originType) &&
-                    Objects.equals(invokeId, te2.invokeId) &&
-                    Objects.equals(data, te2.data)) {
-                return true;
-            }
-        }
-        return false;
+    public int hashCode() {
+        return String.valueOf(this).hashCode();
     }
 
     /**
@@ -204,15 +214,5 @@ public class TriggerEvent implements Serializable {
         return String.valueOf(buf);
     }
 
-    /**
-     * Returns the hash code for this TriggerEvent object.
-     *
-     * @see Object#hashCode()
-     */
-    @Override
-    public int hashCode() {
-        return String.valueOf(this).hashCode();
-    }
-
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/env/AbstractBaseEvaluator.java b/src/main/java/org/apache/commons/scxml2/env/AbstractBaseEvaluator.java
index c87d9006..9cda0040 100644
--- a/src/main/java/org/apache/commons/scxml2/env/AbstractBaseEvaluator.java
+++ b/src/main/java/org/apache/commons/scxml2/env/AbstractBaseEvaluator.java
@@ -40,25 +40,6 @@ public abstract class AbstractBaseEvaluator implements Evaluator, Serializable {
      */
     private static final String ASSIGN_VARIABLE_NAME = "a"+ UUID.randomUUID().toString().replace('-','x');
 
-    /**
-     * @see Evaluator#evalAssign(Context, String, Object)
-     */
-    @Override
-    public void evalAssign(final Context ctx, final String location, final Object data) throws SCXMLExpressionException {
-        final StringBuilder sb = new StringBuilder(location).append("=").append(ASSIGN_VARIABLE_NAME);
-        try {
-            ctx.getVars().put(ASSIGN_VARIABLE_NAME, data);
-            eval(ctx, sb.toString());
-        } catch (final SCXMLExpressionException e) {
-            if (e.getCause() != null && e.getCause() != null && e.getCause().getMessage() != null) {
-                throw new SCXMLExpressionException("Error evaluating assign to location=\"" + location + "\": " + e.getCause().getMessage());
-            }
-            throw e;
-        } finally {
-            ctx.getVars().remove(ASSIGN_VARIABLE_NAME);
-        }
-    }
-
     @Override
     public Object cloneData(final Object data) {
         if (data != null) {
@@ -104,4 +85,23 @@ public abstract class AbstractBaseEvaluator implements Evaluator, Serializable {
     protected Object cloneUnknownDataType(final Object data) {
         return data.toString();
     }
+
+    /**
+     * @see Evaluator#evalAssign(Context, String, Object)
+     */
+    @Override
+    public void evalAssign(final Context ctx, final String location, final Object data) throws SCXMLExpressionException {
+        final StringBuilder sb = new StringBuilder(location).append("=").append(ASSIGN_VARIABLE_NAME);
+        try {
+            ctx.getVars().put(ASSIGN_VARIABLE_NAME, data);
+            eval(ctx, sb.toString());
+        } catch (final SCXMLExpressionException e) {
+            if (e.getCause() != null && e.getCause() != null && e.getCause().getMessage() != null) {
+                throw new SCXMLExpressionException("Error evaluating assign to location=\"" + location + "\": " + e.getCause().getMessage());
+            }
+            throw e;
+        } finally {
+            ctx.getVars().remove(ASSIGN_VARIABLE_NAME);
+        }
+    }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/env/AbstractStateMachine.java b/src/main/java/org/apache/commons/scxml2/env/AbstractStateMachine.java
index 3831b723..a2fa703c 100644
--- a/src/main/java/org/apache/commons/scxml2/env/AbstractStateMachine.java
+++ b/src/main/java/org/apache/commons/scxml2/env/AbstractStateMachine.java
@@ -66,19 +66,44 @@ import org.apache.commons.scxml2.model.TransitionTarget;
 public abstract class AbstractStateMachine {
 
     /**
-     * The state machine that will drive the instances of this class.
+     * A SCXMLListener that is only concerned about &quot;onentry&quot;
+     * notifications.
      */
-    private SCXML stateMachine;
+    protected class EntryListener implements SCXMLListener {
 
-    /**
-     * The instance specific SCXML engine.
-     */
-    private SCXMLExecutor engine;
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void onEntry(final EnterableState entered) {
+            invoke(entered.getId());
+        }
 
-    /**
-     * The log.
-     */
-    private Log log;
+        /**
+         * No-op.
+         *
+         * @param exited The state being exited.
+         */
+        @Override
+        public void onExit(final EnterableState exited) {
+            // nothing to do
+        }
+
+        /**
+         * No-op.
+         *
+         * @param from The &quot;source&quot; transition target.
+         * @param to The &quot;destination&quot; transition target.
+         * @param transition The transition being followed.
+         * @param event The event triggering the transition
+         */
+        @Override
+        public void onTransition(final TransitionTarget from,
+                final TransitionTarget to, final Transition transition, final String event) {
+            // nothing to do
+        }
+
+    }
 
     /**
      * The method signature for the activities corresponding to each
@@ -93,41 +118,19 @@ public abstract class AbstractStateMachine {
     private static final Object[] PARAMETERS = new Object[0];
 
     /**
-     * Convenience constructor, object instantiation incurs parsing cost.
-     *
-     * @param scxmlDocument The URL pointing to the SCXML document that
-     *                      describes the &quot;lifecycle&quot; of the
-     *                      instances of this class.
-     * @throws ModelException in case there is a fatal SCXML object model problem
+     * The state machine that will drive the instances of this class.
      */
-    public AbstractStateMachine(final URL scxmlDocument) throws ModelException {
-        // default is JEXL
-        this(scxmlDocument, new JexlContext(), new JexlEvaluator());
-    }
+    private SCXML stateMachine;
 
     /**
-     * Primary constructor, object instantiation incurs parsing cost.
-     *
-     * @param scxmlDocument The URL pointing to the SCXML document that
-     *                      describes the &quot;lifecycle&quot; of the
-     *                      instances of this class.
-     * @param rootCtx The root context for this instance.
-     * @param evaluator The expression evaluator for this instance.
-     * @throws ModelException in case there is a fatal SCXML object model problem
-     *
-     * @see Context
-     * @see Evaluator
+     * The instance specific SCXML engine.
      */
-    public AbstractStateMachine(final URL scxmlDocument,
-            final Context rootCtx, final Evaluator evaluator) throws ModelException {
-        log = LogFactory.getLog(this.getClass());
-        try {
-            stateMachine = SCXMLReader.read(scxmlDocument);
-        } catch (IOException | XMLStreamException | ModelException ioe) {
-            logError(ioe);
-        }
-        initialize(stateMachine, rootCtx, evaluator);
-    }
+    private SCXMLExecutor engine;
+
+    /**
+     * The log.
+     */
+    private Log log;
 
     /**
      * Convenience constructor.
@@ -164,25 +167,40 @@ public abstract class AbstractStateMachine {
     }
 
     /**
-     * Instantiate and initialize the underlying executor instance.
+     * Convenience constructor, object instantiation incurs parsing cost.
      *
-     * @param stateMachine The state machine
-     * @param rootCtx The root context
-     * @param evaluator The expression evaluator
+     * @param scxmlDocument The URL pointing to the SCXML document that
+     *                      describes the &quot;lifecycle&quot; of the
+     *                      instances of this class.
      * @throws ModelException in case there is a fatal SCXML object model problem
      */
-    private void initialize(final SCXML stateMachine,
+    public AbstractStateMachine(final URL scxmlDocument) throws ModelException {
+        // default is JEXL
+        this(scxmlDocument, new JexlContext(), new JexlEvaluator());
+    }
+
+    /**
+     * Primary constructor, object instantiation incurs parsing cost.
+     *
+     * @param scxmlDocument The URL pointing to the SCXML document that
+     *                      describes the &quot;lifecycle&quot; of the
+     *                      instances of this class.
+     * @param rootCtx The root context for this instance.
+     * @param evaluator The expression evaluator for this instance.
+     * @throws ModelException in case there is a fatal SCXML object model problem
+     *
+     * @see Context
+     * @see Evaluator
+     */
+    public AbstractStateMachine(final URL scxmlDocument,
             final Context rootCtx, final Evaluator evaluator) throws ModelException {
-        engine = new SCXMLExecutor(evaluator, new SimpleDispatcher(),
-            new SimpleErrorReporter());
-        engine.setStateMachine(stateMachine);
-        engine.setRootContext(rootCtx);
-        engine.addListener(stateMachine, new EntryListener());
+        log = LogFactory.getLog(this.getClass());
         try {
-            engine.go();
-        } catch (final ModelException me) {
-            logError(me);
+            stateMachine = SCXMLReader.read(scxmlDocument);
+        } catch (IOException | XMLStreamException | ModelException ioe) {
+            logError(ioe);
         }
+        initialize(stateMachine, rootCtx, evaluator);
     }
 
     /**
@@ -222,12 +240,25 @@ public abstract class AbstractStateMachine {
     }
 
     /**
-     * Sets the log for this class.
+     * Instantiate and initialize the underlying executor instance.
      *
-     * @param log The log to set.
+     * @param stateMachine The state machine
+     * @param rootCtx The root context
+     * @param evaluator The expression evaluator
+     * @throws ModelException in case there is a fatal SCXML object model problem
      */
-    public void setLog(final Log log) {
-        this.log = log;
+    private void initialize(final SCXML stateMachine,
+            final Context rootCtx, final Evaluator evaluator) throws ModelException {
+        engine = new SCXMLExecutor(evaluator, new SimpleDispatcher(),
+            new SimpleErrorReporter());
+        engine.setStateMachine(stateMachine);
+        engine.setRootContext(rootCtx);
+        engine.addListener(stateMachine, new EntryListener());
+        try {
+            engine.go();
+        } catch (final ModelException me) {
+            logError(me);
+        }
     }
 
     /**
@@ -249,6 +280,17 @@ public abstract class AbstractStateMachine {
         return true;
     }
 
+    /**
+     * Utility method for logging error.
+     *
+     * @param exception The exception leading to this error condition.
+     */
+    protected void logError(final Exception exception) {
+        if (log.isErrorEnabled()) {
+            log.error(exception.getMessage(), exception);
+        }
+    }
+
     /**
      * Reset the state machine.
      *
@@ -265,54 +307,12 @@ public abstract class AbstractStateMachine {
     }
 
     /**
-     * Utility method for logging error.
+     * Sets the log for this class.
      *
-     * @param exception The exception leading to this error condition.
-     */
-    protected void logError(final Exception exception) {
-        if (log.isErrorEnabled()) {
-            log.error(exception.getMessage(), exception);
-        }
-    }
-
-    /**
-     * A SCXMLListener that is only concerned about &quot;onentry&quot;
-     * notifications.
+     * @param log The log to set.
      */
-    protected class EntryListener implements SCXMLListener {
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void onEntry(final EnterableState entered) {
-            invoke(entered.getId());
-        }
-
-        /**
-         * No-op.
-         *
-         * @param from The &quot;source&quot; transition target.
-         * @param to The &quot;destination&quot; transition target.
-         * @param transition The transition being followed.
-         * @param event The event triggering the transition
-         */
-        @Override
-        public void onTransition(final TransitionTarget from,
-                final TransitionTarget to, final Transition transition, final String event) {
-            // nothing to do
-        }
-
-        /**
-         * No-op.
-         *
-         * @param exited The state being exited.
-         */
-        @Override
-        public void onExit(final EnterableState exited) {
-            // nothing to do
-        }
-
+    public void setLog(final Log log) {
+        this.log = log;
     }
 
 }
diff --git a/src/main/java/org/apache/commons/scxml2/env/EffectiveContextMap.java b/src/main/java/org/apache/commons/scxml2/env/EffectiveContextMap.java
index 97b9fc39..794a68ab 100644
--- a/src/main/java/org/apache/commons/scxml2/env/EffectiveContextMap.java
+++ b/src/main/java/org/apache/commons/scxml2/env/EffectiveContextMap.java
@@ -61,6 +61,23 @@ public final class EffectiveContextMap extends AbstractMap<String, Object> imple
         return Collections.unmodifiableMap(map).entrySet();
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Object get(final Object key) {
+        if (key != null) {
+            Context current = leaf;
+            while (current != null) {
+                if (current.getVars().containsKey(key.toString())) {
+                    return current.getVars().get(key);
+                }
+                current = current.getParent();
+            }
+        }
+        return null;
+    }
+
     /**
      * Parent Context first merging of all Context vars, to ensure same named 'local' vars shadows parent var
      * @param leaf current leaf Context
@@ -86,21 +103,4 @@ public final class EffectiveContextMap extends AbstractMap<String, Object> imple
         }
         return old;
     }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Object get(final Object key) {
-        if (key != null) {
-            Context current = leaf;
-            while (current != null) {
-                if (current.getVars().containsKey(key.toString())) {
-                    return current.getVars().get(key);
-                }
-                current = current.getParent();
-            }
-        }
-        return null;
-    }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/env/LogUtils.java b/src/main/java/org/apache/commons/scxml2/env/LogUtils.java
index 139afc59..d30c2d2e 100644
--- a/src/main/java/org/apache/commons/scxml2/env/LogUtils.java
+++ b/src/main/java/org/apache/commons/scxml2/env/LogUtils.java
@@ -24,6 +24,23 @@ import org.apache.commons.scxml2.model.TransitionTarget;
  */
 public final class LogUtils {
 
+    /**
+     * Write out this TransitionTarget location in a XPath style format.
+     *
+     * @param tt The TransitionTarget whose &quot;path&quot; is to needed
+     * @return String The XPath style location of the TransitionTarget within
+     *                the SCXML document
+     */
+    public static String getTTPath(final TransitionTarget tt) {
+        final StringBuilder sb = new StringBuilder("/");
+        for (int i = 0; i < tt.getNumberOfAncestors(); i++) {
+            sb.append(tt.getAncestor(i).getId());
+            sb.append("/");
+        }
+        sb.append(tt.getId());
+        return sb.toString();
+    }
+
     /**
      * Create a human readable log view of this transition.
      *
@@ -42,23 +59,6 @@ public final class LogUtils {
                 ')';
     }
 
-    /**
-     * Write out this TransitionTarget location in a XPath style format.
-     *
-     * @param tt The TransitionTarget whose &quot;path&quot; is to needed
-     * @return String The XPath style location of the TransitionTarget within
-     *                the SCXML document
-     */
-    public static String getTTPath(final TransitionTarget tt) {
-        final StringBuilder sb = new StringBuilder("/");
-        for (int i = 0; i < tt.getNumberOfAncestors(); i++) {
-            sb.append(tt.getAncestor(i).getId());
-            sb.append("/");
-        }
-        sb.append(tt.getId());
-        return sb.toString();
-    }
-
     /**
      * Discourage instantiation since this is a utility class.
      */
diff --git a/src/main/java/org/apache/commons/scxml2/env/SimpleContext.java b/src/main/java/org/apache/commons/scxml2/env/SimpleContext.java
index 5d0811c6..f9b37942 100644
--- a/src/main/java/org/apache/commons/scxml2/env/SimpleContext.java
+++ b/src/main/java/org/apache/commons/scxml2/env/SimpleContext.java
@@ -75,26 +75,6 @@ public class SimpleContext implements Context, Serializable {
         }
     }
 
-    /**
-     * Assigns a new value to an existing variable or creates a new one.
-     * The method searches the chain of parent Contexts for variable
-     * existence.
-     *
-     * @param name The variable name
-     * @param value The variable value
-     * @see org.apache.commons.scxml2.Context#set(String, Object)
-     */
-    @Override
-    public void set(final String name, final Object value) {
-        if (getVars().containsKey(name)) { //first try to override local
-            setLocal(name, value);
-        } else if (parent != null && parent.has(name)) { //then check for global
-            parent.set(name, value);
-        } else { //otherwise create a new local variable
-            setLocal(name, value);
-        }
-    }
-
     /**
      * Gets the value of this variable; delegating to parent.
      *
@@ -114,6 +94,46 @@ public class SimpleContext implements Context, Serializable {
         return null;
     }
 
+    /**
+     * Gets the log used by this <code>Context</code> instance.
+     *
+     * @return Log The log being used.
+     */
+    protected Log getLog() {
+        return log;
+    }
+
+    /**
+     * Gets the parent Context, may be null.
+     *
+     * @return Context The parent Context
+     * @see org.apache.commons.scxml2.Context#getParent()
+     */
+    @Override
+    public Context getParent() {
+        return parent;
+    }
+
+    /**
+     * Gets the SCXMLSystemContext for this Context, should not be null unless this is the root Context
+     *
+     * @return The SCXMLSystemContext in a chained Context environment
+     */
+    @Override
+    public final SCXMLSystemContext getSystemContext() {
+        return systemContext;
+    }
+
+    /**
+     * Gets the Map of all local variables in this Context.
+     *
+     * @return Returns the vars.
+     */
+    @Override
+    public Map<String, Object> getVars() {
+        return vars;
+    }
+
     /**
      * Check if this variable exists, delegating to parent.
      *
@@ -149,24 +169,23 @@ public class SimpleContext implements Context, Serializable {
     }
 
     /**
-     * Gets the parent Context, may be null.
-     *
-     * @return Context The parent Context
-     * @see org.apache.commons.scxml2.Context#getParent()
-     */
-    @Override
-    public Context getParent() {
-        return parent;
-    }
-
-    /**
-     * Gets the SCXMLSystemContext for this Context, should not be null unless this is the root Context
+     * Assigns a new value to an existing variable or creates a new one.
+     * The method searches the chain of parent Contexts for variable
+     * existence.
      *
-     * @return The SCXMLSystemContext in a chained Context environment
+     * @param name The variable name
+     * @param value The variable value
+     * @see org.apache.commons.scxml2.Context#set(String, Object)
      */
     @Override
-    public final SCXMLSystemContext getSystemContext() {
-        return systemContext;
+    public void set(final String name, final Object value) {
+        if (getVars().containsKey(name)) { //first try to override local
+            setLocal(name, value);
+        } else if (parent != null && parent.has(name)) { //then check for global
+            parent.set(name, value);
+        } else { //otherwise create a new local variable
+            setLocal(name, value);
+        }
     }
 
     /**
@@ -194,24 +213,5 @@ public class SimpleContext implements Context, Serializable {
     protected void setVars(final Map<String, Object> vars) {
         this.vars = vars;
     }
-
-    /**
-     * Gets the Map of all local variables in this Context.
-     *
-     * @return Returns the vars.
-     */
-    @Override
-    public Map<String, Object> getVars() {
-        return vars;
-    }
-
-    /**
-     * Gets the log used by this <code>Context</code> instance.
-     *
-     * @return Log The log being used.
-     */
-    protected Log getLog() {
-        return log;
-    }
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/env/SimpleDispatcher.java b/src/main/java/org/apache/commons/scxml2/env/SimpleDispatcher.java
index cd520564..ab4de809 100644
--- a/src/main/java/org/apache/commons/scxml2/env/SimpleDispatcher.java
+++ b/src/main/java/org/apache/commons/scxml2/env/SimpleDispatcher.java
@@ -46,10 +46,7 @@ import org.apache.commons.scxml2.model.ActionExecutionError;
  */
 public class SimpleDispatcher implements EventDispatcher, Serializable {
 
-     /** Serial version UID. */
-    private static final long serialVersionUID = 1L;
-
-    /**
+     /**
      * TimerTask implementation.
      */
     class DelayedEventTask extends TimerTask {
@@ -96,6 +93,9 @@ public class SimpleDispatcher implements EventDispatcher, Serializable {
         }
     }
 
+    /** Serial version UID. */
+    private static final long serialVersionUID = 1L;
+
     /** Implementation independent log category. */
      private static final Log log = LogFactory.getLog(EventDispatcher.class);
 
@@ -105,6 +105,28 @@ public class SimpleDispatcher implements EventDispatcher, Serializable {
      */
     private final Map<String, Timer> timers = Collections.synchronizedMap(new HashMap<String, Timer>());
 
+    /**
+     * @see EventDispatcher#cancel(String)
+     */
+    @Override
+    public void cancel(final String sendId) {
+        if (log.isInfoEnabled()) {
+            log.info("cancel( sendId: " + sendId + ")");
+        }
+        if (!timers.containsKey(sendId)) {
+            return; // done, we don't track this one or its already expired
+        }
+        final Timer timer = timers.get(sendId);
+        if (timer != null) {
+            timer.cancel();
+            if (log.isDebugEnabled()) {
+                log.debug("Cancelled event scheduled by <send> with id '"
+                        + sendId + "'");
+            }
+        }
+        timers.remove(sendId);
+    }
+
     /**
      * Gets the log instance.
      *
@@ -114,6 +136,7 @@ public class SimpleDispatcher implements EventDispatcher, Serializable {
         return log;
     }
 
+
     /**
      * Gets the current timers.
      *
@@ -123,34 +146,11 @@ public class SimpleDispatcher implements EventDispatcher, Serializable {
         return timers;
     }
 
-
     @Override
     public SimpleDispatcher newInstance() {
         return new SimpleDispatcher();
     }
 
-    /**
-     * @see EventDispatcher#cancel(String)
-     */
-    @Override
-    public void cancel(final String sendId) {
-        if (log.isInfoEnabled()) {
-            log.info("cancel( sendId: " + sendId + ")");
-        }
-        if (!timers.containsKey(sendId)) {
-            return; // done, we don't track this one or its already expired
-        }
-        final Timer timer = timers.get(sendId);
-        if (timer != null) {
-            timer.cancel();
-            if (log.isDebugEnabled()) {
-                log.debug("Cancelled event scheduled by <send> with id '"
-                        + sendId + "'");
-            }
-        }
-        timers.remove(sendId);
-    }
-
     /**
     @see EventDispatcher#send(java.util.Map, String, String, String, String, Object, Object, long)
      */
diff --git a/src/main/java/org/apache/commons/scxml2/env/SimpleErrorReporter.java b/src/main/java/org/apache/commons/scxml2/env/SimpleErrorReporter.java
index 648ef6d8..10988861 100644
--- a/src/main/java/org/apache/commons/scxml2/env/SimpleErrorReporter.java
+++ b/src/main/java/org/apache/commons/scxml2/env/SimpleErrorReporter.java
@@ -48,6 +48,28 @@ public class SimpleErrorReporter implements ErrorReporter, Serializable {
     public SimpleErrorReporter() {
     }
 
+    /**
+     * Final handling of the resulting errorMessage build by {@link #onError(String, String, Object)} onError}.
+     * <p>The default implementation write the errorMessage as a warning to the log.</p>
+     *
+     * @param errorCode
+     *            one of the ErrorReporter's constants
+     * @param errDetail
+     *            human readable description
+     * @param errCtx
+     *            typically an SCXML element which caused an error,
+     *            may be accompanied by additional information
+     * @param errorMessage
+     *            human readable detail of the error including the state, transition and data
+     */
+    protected void handleErrorMessage(final String errorCode, final String errDetail,
+                               final Object errCtx, final CharSequence errorMessage) {
+
+        if (log.isWarnEnabled()) {
+            log.warn(errorMessage.toString());
+        }
+    }
+
     /**
      * @see ErrorReporter#onError(String, String, Object)
      */
@@ -117,27 +139,5 @@ public class SimpleErrorReporter implements ErrorReporter, Serializable {
         }
         handleErrorMessage(errorCode, errDetail, errCtx, msg);
     }
-
-    /**
-     * Final handling of the resulting errorMessage build by {@link #onError(String, String, Object)} onError}.
-     * <p>The default implementation write the errorMessage as a warning to the log.</p>
-     *
-     * @param errorCode
-     *            one of the ErrorReporter's constants
-     * @param errDetail
-     *            human readable description
-     * @param errCtx
-     *            typically an SCXML element which caused an error,
-     *            may be accompanied by additional information
-     * @param errorMessage
-     *            human readable detail of the error including the state, transition and data
-     */
-    protected void handleErrorMessage(final String errorCode, final String errDetail,
-                               final Object errCtx, final CharSequence errorMessage) {
-
-        if (log.isWarnEnabled()) {
-            log.warn(errorMessage.toString());
-        }
-    }
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/env/Tracer.java b/src/main/java/org/apache/commons/scxml2/env/Tracer.java
index 24a38542..83b4ac5f 100644
--- a/src/main/java/org/apache/commons/scxml2/env/Tracer.java
+++ b/src/main/java/org/apache/commons/scxml2/env/Tracer.java
@@ -58,15 +58,6 @@ public class Tracer implements ErrorHandler, ErrorReporter,
         xmlReporter = new SimpleXMLReporter();
     }
 
-    /**
-     * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
-     */
-    @Override
-    public void warning(final SAXParseException exception)
-    throws SAXException {
-        errHandler.warning(exception);
-    }
-
     /**
      * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
      */
@@ -86,20 +77,20 @@ public class Tracer implements ErrorHandler, ErrorReporter,
     }
 
     /**
-     * @see ErrorReporter#onError(String, String, Object)
+     * @see SCXMLListener#onEntry(EnterableState)
      */
     @Override
-    public void onError(final String errCode, final String errDetail,
-            final Object errCtx) {
-        errReporter.onError(errCode, errDetail, errCtx);
+    public void onEntry(final EnterableState state) {
+        scxmlListener.onEntry(state);
     }
 
     /**
-     * @see SCXMLListener#onEntry(EnterableState)
+     * @see ErrorReporter#onError(String, String, Object)
      */
     @Override
-    public void onEntry(final EnterableState state) {
-        scxmlListener.onEntry(state);
+    public void onError(final String errCode, final String errDetail,
+            final Object errCtx) {
+        errReporter.onError(errCode, errDetail, errCtx);
     }
 
     /**
@@ -129,5 +120,14 @@ public class Tracer implements ErrorHandler, ErrorReporter,
 		xmlReporter.report(message, errorType, relatedInformation, location);
 	}
 
+    /**
+     * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
+     */
+    @Override
+    public void warning(final SAXParseException exception)
+    throws SAXException {
+        errHandler.warning(exception);
+    }
+
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/env/URLResolver.java b/src/main/java/org/apache/commons/scxml2/env/URLResolver.java
index d3f62051..20edf3c3 100644
--- a/src/main/java/org/apache/commons/scxml2/env/URLResolver.java
+++ b/src/main/java/org/apache/commons/scxml2/env/URLResolver.java
@@ -50,15 +50,14 @@ public class URLResolver implements PathResolver, Serializable {
     }
 
     /**
-     * Uses URL(URL, String) constructor to combine URL's.
-     * @see org.apache.commons.scxml2.PathResolver#resolvePath(String)
+     * @see org.apache.commons.scxml2.PathResolver#getResolver(String)
      */
     @Override
-    public String resolvePath(final String ctxPath) {
+    public PathResolver getResolver(final String ctxPath) {
         URL combined;
         try {
             combined = new URL(baseURL, ctxPath);
-            return combined.toString();
+            return new URLResolver(combined);
         } catch (final MalformedURLException e) {
             log.error("Malformed URL", e);
         }
@@ -66,14 +65,15 @@ public class URLResolver implements PathResolver, Serializable {
     }
 
     /**
-     * @see org.apache.commons.scxml2.PathResolver#getResolver(String)
+     * Uses URL(URL, String) constructor to combine URL's.
+     * @see org.apache.commons.scxml2.PathResolver#resolvePath(String)
      */
     @Override
-    public PathResolver getResolver(final String ctxPath) {
+    public String resolvePath(final String ctxPath) {
         URL combined;
         try {
             combined = new URL(baseURL, ctxPath);
-            return new URLResolver(combined);
+            return combined.toString();
         } catch (final MalformedURLException e) {
             log.error("Malformed URL", e);
         }
diff --git a/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyContext.java b/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyContext.java
index d5191b4f..ae1fb85c 100644
--- a/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyContext.java
+++ b/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyContext.java
@@ -44,13 +44,6 @@ public class GroovyContext extends SimpleContext {
     private GroovyContextBinding binding;
     private Map<String, Object> vars;
 
-    GroovyContextBinding getBinding() {
-        if (binding == null) {
-            binding = new GroovyContextBinding(this);
-        }
-        return  binding;
-    }
-
     /**
      * Constructor.
      */
@@ -58,34 +51,47 @@ public class GroovyContext extends SimpleContext {
     }
 
     /**
-     * Constructor with initial vars.
+     * Constructor with parent context.
      *
      * @param parent The parent context.
-     * @param initialVars The initial set of variables.
      * @param evaluator The groovy evaluator
      */
-    public GroovyContext(final Context parent, final Map<String, Object> initialVars, final GroovyEvaluator evaluator) {
-        super(parent, initialVars);
+    public GroovyContext(final Context parent, final GroovyEvaluator evaluator) {
+        super(parent);
         this.evaluator = evaluator;
     }
 
     /**
-     * Constructor with parent context.
+     * Constructor with initial vars.
      *
      * @param parent The parent context.
+     * @param initialVars The initial set of variables.
      * @param evaluator The groovy evaluator
      */
-    public GroovyContext(final Context parent, final GroovyEvaluator evaluator) {
-        super(parent);
+    public GroovyContext(final Context parent, final Map<String, Object> initialVars, final GroovyEvaluator evaluator) {
+        super(parent, initialVars);
         this.evaluator = evaluator;
     }
 
+    GroovyContextBinding getBinding() {
+        if (binding == null) {
+            binding = new GroovyContextBinding(this);
+        }
+        return  binding;
+    }
+
     protected GroovyEvaluator getGroovyEvaluator() {
         return evaluator;
     }
 
-    protected void setGroovyEvaluator(final GroovyEvaluator evaluator) {
-        this.evaluator = evaluator;
+    protected String getScriptBaseClass() {
+        if (scriptBaseClass != null) {
+            return scriptBaseClass;
+        }
+        if (getParent() instanceof GroovyContext) {
+            return ((GroovyContext)getParent()).getScriptBaseClass();
+        }
+        return null;
     }
 
     @Override
@@ -93,23 +99,36 @@ public class GroovyContext extends SimpleContext {
         return vars;
     }
 
-    @Override
-    protected void setVars(final Map<String, Object> vars) {
-        this.vars = vars;
+    @SuppressWarnings("unchecked")
+    private void readObject(final ObjectInputStream in) throws IOException,ClassNotFoundException {
+        this.scriptBaseClass = (String)in.readObject();
+        this.evaluator = (GroovyEvaluator)in.readObject();
+        this.binding = (GroovyContextBinding)in.readObject();
+        SCInstanceObjectInputStream.ClassResolver currentResolver = null;
+        try {
+            if (evaluator != null && in instanceof SCInstanceObjectInputStream) {
+                currentResolver = ((SCInstanceObjectInputStream)in).setClassResolver(osc -> Class.forName(osc.getName(), true, evaluator.getGroovyClassLoader()));
+            }
+            this.vars = (Map<String, Object>)in.readObject();
+        }
+        finally {
+            if (in instanceof SCInstanceObjectInputStream) {
+                ((SCInstanceObjectInputStream)in).setClassResolver(currentResolver);
+            }
+        }
+    }
+
+    protected void setGroovyEvaluator(final GroovyEvaluator evaluator) {
+        this.evaluator = evaluator;
     }
 
     protected void setScriptBaseClass(final String scriptBaseClass) {
         this.scriptBaseClass = scriptBaseClass;
     }
 
-    protected String getScriptBaseClass() {
-        if (scriptBaseClass != null) {
-            return scriptBaseClass;
-        }
-        if (getParent() instanceof GroovyContext) {
-            return ((GroovyContext)getParent()).getScriptBaseClass();
-        }
-        return null;
+    @Override
+    protected void setVars(final Map<String, Object> vars) {
+        this.vars = vars;
     }
 
     private void writeObject(final ObjectOutputStream out) throws IOException {
@@ -132,23 +151,4 @@ public class GroovyContext extends SimpleContext {
         out.writeObject(this.binding);
         out.writeObject(this.vars);
     }
-
-    @SuppressWarnings("unchecked")
-    private void readObject(final ObjectInputStream in) throws IOException,ClassNotFoundException {
-        this.scriptBaseClass = (String)in.readObject();
-        this.evaluator = (GroovyEvaluator)in.readObject();
-        this.binding = (GroovyContextBinding)in.readObject();
-        SCInstanceObjectInputStream.ClassResolver currentResolver = null;
-        try {
-            if (evaluator != null && in instanceof SCInstanceObjectInputStream) {
-                currentResolver = ((SCInstanceObjectInputStream)in).setClassResolver(osc -> Class.forName(osc.getName(), true, evaluator.getGroovyClassLoader()));
-            }
-            this.vars = (Map<String, Object>)in.readObject();
-        }
-        finally {
-            if (in instanceof SCInstanceObjectInputStream) {
-                ((SCInstanceObjectInputStream)in).setClassResolver(currentResolver);
-            }
-        }
-    }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyContextBinding.java b/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyContextBinding.java
index 79b19785..74ebff5a 100644
--- a/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyContextBinding.java
+++ b/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyContextBinding.java
@@ -43,6 +43,11 @@ public class GroovyContextBinding extends Binding implements Serializable {
         return context;
     }
 
+    @Override
+    public Object getProperty(final String property) {
+        return getVariable(property);
+    }
+
     @Override
     public Object getVariable(final String name) {
         final Object result = context.get(name);
@@ -53,12 +58,8 @@ public class GroovyContextBinding extends Binding implements Serializable {
     }
 
     @Override
-    public void setVariable(final String name, final Object value) {
-        if (context.has(name)) {
-            context.set(name, value);
-        } else {
-            context.setLocal(name, value);
-        }
+    public Map<String, Object> getVariables() {
+        return new LinkedHashMap<>(context.getVars());
     }
 
     @Override
@@ -67,17 +68,16 @@ public class GroovyContextBinding extends Binding implements Serializable {
     }
 
     @Override
-    public Map<String, Object> getVariables() {
-        return new LinkedHashMap<>(context.getVars());
-    }
-
-    @Override
-    public Object getProperty(final String property) {
-        return getVariable(property);
+    public void setProperty(final String property, final Object newValue) {
+        setVariable(property, newValue);
     }
 
     @Override
-    public void setProperty(final String property, final Object newValue) {
-        setVariable(property, newValue);
+    public void setVariable(final String name, final Object value) {
+        if (context.has(name)) {
+            context.set(name, value);
+        } else {
+            context.setLocal(name, value);
+        }
     }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyEvaluator.java b/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyEvaluator.java
index 897ba124..5194d969 100644
--- a/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyEvaluator.java
+++ b/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyEvaluator.java
@@ -41,18 +41,8 @@ import org.apache.commons.scxml2.model.SCXML;
  */
 public class GroovyEvaluator extends AbstractBaseEvaluator {
 
-    /** Serial version UID. */
-    private static final long serialVersionUID = 1L;
-
-    public static final String SUPPORTED_DATA_MODEL = "groovy";
-
     public static class GroovyEvaluatorProvider implements EvaluatorProvider {
 
-        @Override
-        public String getSupportedDatamodel() {
-            return SUPPORTED_DATA_MODEL;
-        }
-
         @Override
         public Evaluator getEvaluator() {
             return new GroovyEvaluator();
@@ -62,8 +52,18 @@ public class GroovyEvaluator extends AbstractBaseEvaluator {
         public Evaluator getEvaluator(final SCXML document) {
             return new GroovyEvaluator();
         }
+
+        @Override
+        public String getSupportedDatamodel() {
+            return SUPPORTED_DATA_MODEL;
+        }
     }
 
+    /** Serial version UID. */
+    private static final long serialVersionUID = 1L;
+
+    public static final String SUPPORTED_DATA_MODEL = "groovy";
+
     /** Error message if evaluation context is not a GroovyContext. */
     private static final String ERR_CTX_TYPE = "Error evaluating Groovy "
             + "expression, Context must be a org.apache.commons.scxml2.env.groovy.GroovyContext";
@@ -117,51 +117,11 @@ public class GroovyEvaluator extends AbstractBaseEvaluator {
         this.scriptCache = newScriptCache();
     }
 
-    /**
-     * Overridable factory method to create the GroovyExtendableScriptCache for this GroovyEvaluator.
-     * <p>
-     * The default implementation configures the scriptCache to use the {@link #scriptPreProcessor GroovyEvaluator scriptPreProcessor}
-     * and the {@link GroovySCXMLScript} as script base class.
-     * </p>
-     *
-     * @return GroovyExtendableScriptCache for this GroovyEvaluator
-     */
-    protected GroovyExtendableScriptCache newScriptCache() {
-        final GroovyExtendableScriptCache scriptCache = new GroovyExtendableScriptCache();
-        scriptCache.setScriptPreProcessor(getScriptPreProcessor());
-        scriptCache.setScriptBaseClass(GroovySCXMLScript.class.getName());
-        return scriptCache;
-    }
-
-    @SuppressWarnings("unchecked")
-    protected Script getScript(final GroovyContext groovyContext, final String scriptBaseClassName, final String scriptSource) {
-        final Script script = scriptCache.getScript(scriptBaseClassName, scriptSource);
-        script.setBinding(groovyContext.getBinding());
-        return script;
-    }
-
     @SuppressWarnings("unused")
     public void clearCache() {
         scriptCache.clearCache();
     }
 
-    public GroovyExtendableScriptCache.ScriptPreProcessor getScriptPreProcessor() {
-        return scriptPreProcessor;
-    }
-
-    /* SCXMLEvaluator implementation methods */
-
-
-    @Override
-    public String getSupportedDatamodel() {
-        return SUPPORTED_DATA_MODEL;
-    }
-
-    @Override
-    public boolean requiresGlobalContext() {
-        return false;
-    }
-
     /**
      * Evaluate an expression.
      *
@@ -252,10 +212,42 @@ public class GroovyEvaluator extends AbstractBaseEvaluator {
         }
     }
 
+    /* SCXMLEvaluator implementation methods */
+
+
+    /**
+     * Create a new context which is the summation of contexts from the
+     * current state to document root, child has priority over parent
+     * in scoping rules.
+     *
+     * @param nodeCtx The GroovyContext for this state.
+     * @return The effective GroovyContext for the path leading up to
+     *         document root.
+     */
+    protected GroovyContext getEffectiveContext(final GroovyContext nodeCtx) {
+        return new GroovyContext(nodeCtx, new EffectiveContextMap(nodeCtx), this);
+    }
+
     protected ClassLoader getGroovyClassLoader() {
         return scriptCache.getGroovyClassLoader();
     }
 
+    @SuppressWarnings("unchecked")
+    protected Script getScript(final GroovyContext groovyContext, final String scriptBaseClassName, final String scriptSource) {
+        final Script script = scriptCache.getScript(scriptBaseClassName, scriptSource);
+        script.setBinding(groovyContext.getBinding());
+        return script;
+    }
+
+    public GroovyExtendableScriptCache.ScriptPreProcessor getScriptPreProcessor() {
+        return scriptPreProcessor;
+    }
+
+    @Override
+    public String getSupportedDatamodel() {
+        return SUPPORTED_DATA_MODEL;
+    }
+
     /**
      * Create a new child context.
      *
@@ -269,15 +261,23 @@ public class GroovyEvaluator extends AbstractBaseEvaluator {
     }
 
     /**
-     * Create a new context which is the summation of contexts from the
-     * current state to document root, child has priority over parent
-     * in scoping rules.
+     * Overridable factory method to create the GroovyExtendableScriptCache for this GroovyEvaluator.
+     * <p>
+     * The default implementation configures the scriptCache to use the {@link #scriptPreProcessor GroovyEvaluator scriptPreProcessor}
+     * and the {@link GroovySCXMLScript} as script base class.
+     * </p>
      *
-     * @param nodeCtx The GroovyContext for this state.
-     * @return The effective GroovyContext for the path leading up to
-     *         document root.
+     * @return GroovyExtendableScriptCache for this GroovyEvaluator
      */
-    protected GroovyContext getEffectiveContext(final GroovyContext nodeCtx) {
-        return new GroovyContext(nodeCtx, new EffectiveContextMap(nodeCtx), this);
+    protected GroovyExtendableScriptCache newScriptCache() {
+        final GroovyExtendableScriptCache scriptCache = new GroovyExtendableScriptCache();
+        scriptCache.setScriptPreProcessor(getScriptPreProcessor());
+        scriptCache.setScriptBaseClass(GroovySCXMLScript.class.getName());
+        return scriptCache;
+    }
+
+    @Override
+    public boolean requiresGlobalContext() {
+        return false;
     }
 }
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyExtendableScriptCache.java b/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyExtendableScriptCache.java
index 3b5d983c..3d135f38 100644
--- a/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyExtendableScriptCache.java
+++ b/src/main/java/org/apache/commons/scxml2/env/groovy/GroovyExtendableScriptCache.java
@@ -70,16 +70,6 @@ import org.codehaus.groovy.control.CompilerConfiguration;
  */
 public class GroovyExtendableScriptCache implements Serializable {
 
-    private static final long serialVersionUID = 1L;
-
-    /**
-     * Serializable factory interface providing the Groovy parent ClassLoader,
-     * needed to restore the specific ClassLoader after de-serialization
-     */
-    public interface ParentClassLoaderFactory extends Serializable {
-        ClassLoader getClassLoader();
-    }
-
     /**
      * Serializable factory interface providing the Groovy CompilerConfiguration,
      * needed to restore the specific CompilerConfiguration after de-serialization
@@ -88,21 +78,14 @@ public class GroovyExtendableScriptCache implements Serializable {
         CompilerConfiguration getCompilerConfiguration();
     }
 
-    public interface ScriptPreProcessor extends Serializable {
-        String preProcess(String script);
+    /**
+     * Serializable factory interface providing the Groovy parent ClassLoader,
+     * needed to restore the specific ClassLoader after de-serialization
+     */
+    public interface ParentClassLoaderFactory extends Serializable {
+        ClassLoader getClassLoader();
     }
 
-    /** Default CodeSource code base for the compiled Groovy scripts */
-    public static final String DEFAULT_SCRIPT_CODE_BASE = "/groovy/scxml/script";
-
-    /** Default factory for the Groovy parent ClassLoader, returning this class its ClassLoader */
-    public static final ParentClassLoaderFactory DEFAULT_PARENT_CLASS_LOADER_FACTORY =
-            (ParentClassLoaderFactory) GroovyExtendableScriptCache.class::getClassLoader;
-
-    /** Default factory for the Groovy CompilerConfiguration, returning a new and unmodified CompilerConfiguration instance */
-    public static final CompilerConfigurationFactory DEFAULT_COMPILER_CONFIGURATION_FACTORY =
-            (CompilerConfigurationFactory) CompilerConfiguration::new;
-
     protected static class ScriptCacheElement implements Serializable {
         private static final long serialVersionUID = 1L;
 
@@ -116,30 +99,6 @@ public class GroovyExtendableScriptCache implements Serializable {
             this.scriptSource = scriptSource;
         }
 
-        public String getBaseClass() {
-            return baseClass;
-        }
-
-        public String getScriptSource() {
-            return scriptSource;
-        }
-
-        public String getScriptName() {
-            return scriptName;
-        }
-
-        public void setScriptName(final String scriptName) {
-            this.scriptName = scriptName;
-        }
-
-        public Class<? extends Script> getScriptClass() {
-            return scriptClass;
-        }
-
-        public void setScriptClass(final Class<? extends Script> scriptClass) {
-            this.scriptClass = scriptClass;
-        }
-
         @Override
         public boolean equals(final Object o) {
             if (this == o) {
@@ -156,14 +115,55 @@ public class GroovyExtendableScriptCache implements Serializable {
 
         }
 
+        public String getBaseClass() {
+            return baseClass;
+        }
+
+        public Class<? extends Script> getScriptClass() {
+            return scriptClass;
+        }
+
+        public String getScriptName() {
+            return scriptName;
+        }
+
+        public String getScriptSource() {
+            return scriptSource;
+        }
+
         @Override
         public int hashCode() {
             int result = baseClass != null ? baseClass.hashCode() : 0;
             result = 31 * result + scriptSource.hashCode();
             return result;
         }
+
+        public void setScriptClass(final Class<? extends Script> scriptClass) {
+            this.scriptClass = scriptClass;
+        }
+
+        public void setScriptName(final String scriptName) {
+            this.scriptName = scriptName;
+        }
+    }
+
+    public interface ScriptPreProcessor extends Serializable {
+        String preProcess(String script);
     }
 
+    private static final long serialVersionUID = 1L;
+
+    /** Default CodeSource code base for the compiled Groovy scripts */
+    public static final String DEFAULT_SCRIPT_CODE_BASE = "/groovy/scxml/script";
+
+    /** Default factory for the Groovy parent ClassLoader, returning this class its ClassLoader */
+    public static final ParentClassLoaderFactory DEFAULT_PARENT_CLASS_LOADER_FACTORY =
+            (ParentClassLoaderFactory) GroovyExtendableScriptCache.class::getClassLoader;
+
+    /** Default factory for the Groovy CompilerConfiguration, returning a new and unmodified CompilerConfiguration instance */
+    public static final CompilerConfigurationFactory DEFAULT_COMPILER_CONFIGURATION_FACTORY =
+            (CompilerConfigurationFactory) CompilerConfiguration::new;
+
     private final LinkedHashMap<ScriptCacheElement, ScriptCacheElement> scriptCache = new LinkedHashMap<>();
 
     private String scriptCodeBase = DEFAULT_SCRIPT_CODE_BASE;
@@ -179,19 +179,68 @@ public class GroovyExtendableScriptCache implements Serializable {
     public GroovyExtendableScriptCache() {
     }
 
-    /*
-     * Hook into the de-serialization process, reloading the transient GroovyClassLoader, CompilerConfiguration and
-     * re-generate Script classes through {@link #ensureInitializedOrReloaded()}
-     */
-    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
-        in.defaultReadObject();
-        ensureInitializedOrReloaded();
+    public void clearCache() {
+        synchronized (scriptCache) {
+            scriptCache.clear();
+            if (groovyClassLoader != null) {
+                groovyClassLoader.clearCache();
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected Class<Script> compileScript(final String scriptBaseClass, final String scriptSource, final String scriptName) {
+        final String script = preProcessScript(scriptSource);
+
+        final GroovyCodeSource codeSource = AccessController.doPrivileged((PrivilegedAction<GroovyCodeSource>)
+                () -> new GroovyCodeSource(script, scriptName, getScriptCodeBase()));
+
+        final String currentScriptBaseClass = compilerConfiguration.getScriptBaseClass();
+        try {
+            if (scriptBaseClass != null) {
+                compilerConfiguration.setScriptBaseClass(scriptBaseClass);
+            }
+            return groovyClassLoader.parseClass(codeSource, false);
+        }
+        finally {
+            compilerConfiguration.setScriptBaseClass(currentScriptBaseClass);
+        }
+    }
+
+    protected void ensureInitializedOrReloaded() {
+        if (groovyClassLoader == null) {
+            compilerConfiguration = new CompilerConfiguration(getCompilerConfigurationFactory().getCompilerConfiguration());
+            if (getScriptBaseClass() != null) {
+                compilerConfiguration.setScriptBaseClass(getScriptBaseClass());
+            }
+
+            groovyClassLoader = AccessController.doPrivileged((PrivilegedAction<GroovyClassLoader>)
+                    () -> new GroovyClassLoader(getParentClassLoaderFactory().getClassLoader(), compilerConfiguration));
+            if (!scriptCache.isEmpty()) {
+                // de-serialized: need to re-generate all previously compiled scripts (this can cause a hick-up...):
+                for (final ScriptCacheElement element : scriptCache.keySet()) {
+                    element.setScriptClass(compileScript(element.getBaseClass(), element.getScriptSource(), element.getScriptName()));
+                }
+            }
+        }
+    }
+
+    protected String generatedScriptName(final String scriptSource, final int seed) {
+        return "script"+seed+"_"+Math.abs(scriptSource.hashCode())+".groovy";
+    }
+
+    public CompilerConfigurationFactory getCompilerConfigurationFactory() {
+        return compilerConfigurationFactory;
     }
 
     public ClassLoader getGroovyClassLoader() {
         return groovyClassLoader;
     }
 
+    public ParentClassLoaderFactory getParentClassLoaderFactory() {
+        return parentClassLoaderFactory;
+    }
+
     /**
      * @param scriptSource The script source, which will optionally be first preprocessed through {@link #preProcessScript(String)}
      *                     using the configured {@link #getScriptPreProcessor}
@@ -226,40 +275,22 @@ public class GroovyExtendableScriptCache implements Serializable {
         }
     }
 
-    protected void ensureInitializedOrReloaded() {
-        if (groovyClassLoader == null) {
-            compilerConfiguration = new CompilerConfiguration(getCompilerConfigurationFactory().getCompilerConfiguration());
-            if (getScriptBaseClass() != null) {
-                compilerConfiguration.setScriptBaseClass(getScriptBaseClass());
-            }
-
-            groovyClassLoader = AccessController.doPrivileged((PrivilegedAction<GroovyClassLoader>)
-                    () -> new GroovyClassLoader(getParentClassLoaderFactory().getClassLoader(), compilerConfiguration));
-            if (!scriptCache.isEmpty()) {
-                // de-serialized: need to re-generate all previously compiled scripts (this can cause a hick-up...):
-                for (final ScriptCacheElement element : scriptCache.keySet()) {
-                    element.setScriptClass(compileScript(element.getBaseClass(), element.getScriptSource(), element.getScriptName()));
-                }
-            }
-        }
+    public String getScriptBaseClass() {
+        return scriptBaseClass;
     }
 
-    @SuppressWarnings("unchecked")
-    protected Class<Script> compileScript(final String scriptBaseClass, final String scriptSource, final String scriptName) {
-        final String script = preProcessScript(scriptSource);
+    /** @return The current configured CodeSource code base used for the compilation of the Groovy scripts */
+    public String getScriptCodeBase() {
+        return scriptCodeBase;
+    }
 
-        final GroovyCodeSource codeSource = AccessController.doPrivileged((PrivilegedAction<GroovyCodeSource>)
-                () -> new GroovyCodeSource(script, scriptName, getScriptCodeBase()));
+    public ScriptPreProcessor getScriptPreProcessor() {
+        return scriptPreProcessor;
+    }
 
-        final String currentScriptBaseClass = compilerConfiguration.getScriptBaseClass();
-        try {
-            if (scriptBaseClass != null) {
-                compilerConfiguration.setScriptBaseClass(scriptBaseClass);
-            }
-            return groovyClassLoader.parseClass(codeSource, false);
-        }
-        finally {
-            compilerConfiguration.setScriptBaseClass(currentScriptBaseClass);
+    public boolean isEmpty() {
+        synchronized (scriptCache) {
+            return scriptCache.isEmpty();
         }
     }
 
@@ -267,13 +298,27 @@ public class GroovyExtendableScriptCache implements Serializable {
         return getScriptPreProcessor() != null ? getScriptPreProcessor().preProcess(scriptSource) : scriptSource;
     }
 
-    protected String generatedScriptName(final String scriptSource, final int seed) {
-        return "script"+seed+"_"+Math.abs(scriptSource.hashCode())+".groovy";
+    /*
+     * Hook into the de-serialization process, reloading the transient GroovyClassLoader, CompilerConfiguration and
+     * re-generate Script classes through {@link #ensureInitializedOrReloaded()}
+     */
+    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        ensureInitializedOrReloaded();
     }
 
-    /** @return The current configured CodeSource code base used for the compilation of the Groovy scripts */
-    public String getScriptCodeBase() {
-        return scriptCodeBase;
+    @SuppressWarnings("unused")
+    public void setCompilerConfigurationFactory(final CompilerConfigurationFactory compilerConfigurationFactory) {
+        this.compilerConfigurationFactory = compilerConfigurationFactory != null ? compilerConfigurationFactory : DEFAULT_COMPILER_CONFIGURATION_FACTORY;
+    }
+
+    @SuppressWarnings("unused")
+    public void setParentClassLoaderFactory(final ParentClassLoaderFactory parentClassLoaderFactory) {
+        this.parentClassLoaderFactory = parentClassLoaderFactory != null ? parentClassLoaderFactory : DEFAULT_PARENT_CLASS_LOADER_FACTORY;
+    }
+
+    public void setScriptBaseClass(final String scriptBaseClass) {
+        this.scriptBaseClass = scriptBaseClass;
     }
 
     /**
@@ -290,52 +335,7 @@ public class GroovyExtendableScriptCache implements Serializable {
             this.scriptCodeBase = DEFAULT_SCRIPT_CODE_BASE;
         }
     }
-
-    public String getScriptBaseClass() {
-        return scriptBaseClass;
-    }
-
-    public void setScriptBaseClass(final String scriptBaseClass) {
-        this.scriptBaseClass = scriptBaseClass;
-    }
-
-    public ParentClassLoaderFactory getParentClassLoaderFactory() {
-        return parentClassLoaderFactory;
-    }
-
-    @SuppressWarnings("unused")
-    public void setParentClassLoaderFactory(final ParentClassLoaderFactory parentClassLoaderFactory) {
-        this.parentClassLoaderFactory = parentClassLoaderFactory != null ? parentClassLoaderFactory : DEFAULT_PARENT_CLASS_LOADER_FACTORY;
-    }
-
-    public CompilerConfigurationFactory getCompilerConfigurationFactory() {
-        return compilerConfigurationFactory;
-    }
-
-    @SuppressWarnings("unused")
-    public void setCompilerConfigurationFactory(final CompilerConfigurationFactory compilerConfigurationFactory) {
-        this.compilerConfigurationFactory = compilerConfigurationFactory != null ? compilerConfigurationFactory : DEFAULT_COMPILER_CONFIGURATION_FACTORY;
-    }
-
-    public ScriptPreProcessor getScriptPreProcessor() {
-        return scriptPreProcessor;
-    }
-
     public void setScriptPreProcessor(final ScriptPreProcessor scriptPreProcessor) {
         this.scriptPreProcessor = scriptPreProcessor;
     }
-
-    public boolean isEmpty() {
-        synchronized (scriptCache) {
-            return scriptCache.isEmpty();
-        }
-    }
-    public void clearCache() {
-        synchronized (scriptCache) {
-            scriptCache.clear();
-            if (groovyClassLoader != null) {
-                groovyClassLoader.clearCache();
-            }
-        }
-    }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/env/groovy/GroovySCXMLScript.java b/src/main/java/org/apache/commons/scxml2/env/groovy/GroovySCXMLScript.java
index fc862916..c0723fcb 100644
--- a/src/main/java/org/apache/commons/scxml2/env/groovy/GroovySCXMLScript.java
+++ b/src/main/java/org/apache/commons/scxml2/env/groovy/GroovySCXMLScript.java
@@ -38,10 +38,30 @@ public abstract class GroovySCXMLScript extends Script {
         super(null);
     }
 
-    @Override
-    public void setBinding(final Binding binding) {
-        super.setBinding(binding);
-        this.context = ((GroovyContextBinding) binding).getContext();
+    /**
+     * The empty function mimics the behavior of the JEXL empty function, in that it returns true if the parameter is:
+     * <ul>
+     *     <li>null, or</li>
+     *     <li>an empty String, or</li>
+     *     <li>an zero length Array, or</li>
+     *     <li>an empty Collection, or</li>
+     *     <li>an empty Map</li>
+     * </ul>
+     * <p>
+     *     Note: one difference with the JEXL language is that Groovy doesn't allow checking for undefined variables.<br>
+     *     Before being able to check, Groovy will already have raised an MissingPropertyException if the variable cannot be found.<br>
+     *     To work around this, the custom {@link #var(String)} function is available.
+     * </p>
+     *
+     * @param obj the object to check if it is empty
+     * @return true if the object is empty, false otherwise
+     */
+    public boolean empty(final Object obj) {
+        return obj == null ||
+                (obj instanceof String && ((String)obj).isEmpty()) ||
+                ((obj.getClass().isArray() && Array.getLength(obj)==0)) ||
+                (obj instanceof Collection && ((Collection)obj).size()==0) ||
+                (obj instanceof Map && ((Map)obj).isEmpty());
     }
 
     /**
@@ -53,6 +73,12 @@ public abstract class GroovySCXMLScript extends Script {
         return Builtin.isMember(context, state);
     }
 
+    @Override
+    public void setBinding(final Binding binding) {
+        super.setBinding(binding);
+        this.context = ((GroovyContextBinding) binding).getContext();
+    }
+
     /**
      * The var function can be used to check if a variable is defined,
      * <p>
@@ -85,30 +111,4 @@ public abstract class GroovySCXMLScript extends Script {
         }
         return true;
     }
-
-    /**
-     * The empty function mimics the behavior of the JEXL empty function, in that it returns true if the parameter is:
-     * <ul>
-     *     <li>null, or</li>
-     *     <li>an empty String, or</li>
-     *     <li>an zero length Array, or</li>
-     *     <li>an empty Collection, or</li>
-     *     <li>an empty Map</li>
-     * </ul>
-     * <p>
-     *     Note: one difference with the JEXL language is that Groovy doesn't allow checking for undefined variables.<br>
-     *     Before being able to check, Groovy will already have raised an MissingPropertyException if the variable cannot be found.<br>
-     *     To work around this, the custom {@link #var(String)} function is available.
-     * </p>
-     *
-     * @param obj the object to check if it is empty
-     * @return true if the object is empty, false otherwise
-     */
-    public boolean empty(final Object obj) {
-        return obj == null ||
-                (obj instanceof String && ((String)obj).isEmpty()) ||
-                ((obj.getClass().isArray() && Array.getLength(obj)==0)) ||
-                (obj instanceof Collection && ((Collection)obj).size()==0) ||
-                (obj instanceof Map && ((Map)obj).isEmpty());
-    }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/env/javascript/JSBindings.java b/src/main/java/org/apache/commons/scxml2/env/javascript/JSBindings.java
index 6e520010..ff01077e 100644
--- a/src/main/java/org/apache/commons/scxml2/env/javascript/JSBindings.java
+++ b/src/main/java/org/apache/commons/scxml2/env/javascript/JSBindings.java
@@ -45,16 +45,10 @@ public class JSBindings implements Bindings {
     }
 
     /**
-     * Sets or update the SCXML context delegate
-     *
-     * @param jsContext the SCXML context to use for script variables.
-     * @throws IllegalArgumentException Thrown if <code>jsContext</code> is <code>null</code>.
+     * Does nothing - never invoked anyway
      */
-    public void setContext(final JSContext jsContext) {
-        if (jsContext == null) {
-            throw new IllegalArgumentException("SCXML context is required");
-        }
-        this.context = jsContext;
+    @Override
+    public void clear() {
     }
 
     /**
@@ -65,22 +59,6 @@ public class JSBindings implements Bindings {
         return context.has(key.toString());
     }
 
-    /**
-     * Returns the SCXML context key set
-     */
-    @Override
-    public Set<String> keySet() {
-        return context.getVars().keySet();
-    }
-
-    /**
-     * Returns the size of the SCXML context size.
-     */
-    @Override
-    public int size() {
-        return context.getVars().size();
-    }
-
     /**
      * Returns <code>true</code> if the SCXML context contains <code>value</code>.
      */
@@ -98,11 +76,11 @@ public class JSBindings implements Bindings {
     }
 
     /**
-     * Returns the SCXML context values.
+     * Returns the value from the SCXML context identified by <code>key</code>.
      */
     @Override
-    public Collection<Object> values() {
-        return context.getVars().values();
+    public Object get(final Object key) {
+        return context.get(key.toString());
     }
 
     /**
@@ -114,11 +92,11 @@ public class JSBindings implements Bindings {
     }
 
     /**
-     * Returns the value from the SCXML context identified by <code>key</code>.
+     * Returns the SCXML context key set
      */
     @Override
-    public Object get(final Object key) {
-        return context.get(key.toString());
+    public Set<String> keySet() {
+        return context.getVars().keySet();
     }
 
     /**
@@ -163,9 +141,31 @@ public class JSBindings implements Bindings {
     }
 
     /**
-     * Does nothing - never invoked anyway
+     * Sets or update the SCXML context delegate
+     *
+     * @param jsContext the SCXML context to use for script variables.
+     * @throws IllegalArgumentException Thrown if <code>jsContext</code> is <code>null</code>.
+     */
+    public void setContext(final JSContext jsContext) {
+        if (jsContext == null) {
+            throw new IllegalArgumentException("SCXML context is required");
+        }
+        this.context = jsContext;
+    }
+
+    /**
+     * Returns the size of the SCXML context size.
      */
     @Override
-    public void clear() {
+    public int size() {
+        return context.getVars().size();
+    }
+
+    /**
+     * Returns the SCXML context values.
+     */
+    @Override
+    public Collection<Object> values() {
+        return context.getVars().values();
     }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/env/javascript/JSContext.java b/src/main/java/org/apache/commons/scxml2/env/javascript/JSContext.java
index fb605b77..50e9d177 100644
--- a/src/main/java/org/apache/commons/scxml2/env/javascript/JSContext.java
+++ b/src/main/java/org/apache/commons/scxml2/env/javascript/JSContext.java
@@ -38,6 +38,14 @@ public class JSContext extends SimpleContext {
     public JSContext() {
     }
 
+    /**
+     * Child constructor - Just invokes the identical SimpleContext constructor.
+     * @param parent Parent context for this context.
+     */
+    public JSContext(final Context parent) {
+        super(parent);
+    }
+
     /**
      * Constructor with initial vars - Just invokes the identical SimpleContext constructor.
      * @param parent The parent context
@@ -46,13 +54,5 @@ public class JSContext extends SimpleContext {
     public JSContext(final Context parent, final Map<String, Object> initialVars) {
         super(parent, initialVars);
     }
-
-    /**
-     * Child constructor - Just invokes the identical SimpleContext constructor.
-     * @param parent Parent context for this context.
-     */
-    public JSContext(final Context parent) {
-        super(parent);
-    }
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/env/javascript/JSEvaluator.java b/src/main/java/org/apache/commons/scxml2/env/javascript/JSEvaluator.java
index 71730276..6cc982bf 100644
--- a/src/main/java/org/apache/commons/scxml2/env/javascript/JSEvaluator.java
+++ b/src/main/java/org/apache/commons/scxml2/env/javascript/JSEvaluator.java
@@ -55,15 +55,8 @@ import org.apache.commons.scxml2.model.SCXML;
  */
 public class JSEvaluator extends AbstractBaseEvaluator {
 
-    public static final String SUPPORTED_DATA_MODEL = Evaluator.ECMASCRIPT_DATA_MODEL;
-
     public static class JSEvaluatorProvider implements EvaluatorProvider {
 
-        @Override
-        public String getSupportedDatamodel() {
-            return SUPPORTED_DATA_MODEL;
-        }
-
         @Override
         public Evaluator getEvaluator() {
             return new JSEvaluator();
@@ -73,130 +66,48 @@ public class JSEvaluator extends AbstractBaseEvaluator {
         public Evaluator getEvaluator(final SCXML document) {
             return new JSEvaluator();
         }
+
+        @Override
+        public String getSupportedDatamodel() {
+            return SUPPORTED_DATA_MODEL;
+        }
     }
 
+    public static final String SUPPORTED_DATA_MODEL = Evaluator.ECMASCRIPT_DATA_MODEL;
+
     private static final String SCXML_SYSTEM_CONTEXT = "_scxmlSystemContext";
 
     /** Error message if evaluation context is not a JexlContext. */
     private static final String ERR_CTX_TYPE = "Error evaluating JavaScript "
             + "expression, Context must be a org.apache.commons.scxml2.env.javascript.JSContext";
 
-    /** Nashorn ScriptEngine **/
-    private transient ScriptEngine engine;
-
     /** Nashorn Global initialization script, loaded from <code>init_global.js</code> classpath resource */
     private static String initGlobalsScript;
 
+    /** Nashorn ScriptEngine **/
+    private transient ScriptEngine engine;
+
     /** ScriptContext for a single SCXML instance (JSEvaluator also cannot be shared between SCXML instances) */
     private transient ScriptContext scriptContext;
 
     /**
-     * Initialize the singleton Javascript ScriptEngine to be used with a separate ScriptContext for each SCXML instance
-     * not sharing their global scope, see {@link #getScriptContext(JSContext)}.
-     * <p>
-     * The SCXML required protected system variables and (possible) other Javascript global initializations are defined
-     * in a <code>init_global.js</code> script which is pre-loaded as (classpath) resource, to be executed once during
-     * initialization of a new Javascript (Nashorn) Global.
-     * </p>
+     * Copy the Javscript global context (i.e. nashorn Global instance) variables to SCXML {@code jsContext}
+     * in order to make sure all the new global variables set by the JavaScript engine after evaluation are
+     * available from {@link JSContext} instance as well.
+     * <p>Note: the internal <code>"_scxmlSystemContext</code> variable is always skipped.</p>
+     * @param global The Javascript Bindings holding the Javascript Global context variables
+     * @param jsContext The SCXML context to copy/merge the variables into
      */
-    protected synchronized void initEngine() {
-        if (engine == null) {
-            engine = new ScriptEngineManager().getEngineByName("JavaScript");
-            if (initGlobalsScript == null) {
-                try {
-                    initGlobalsScript = IOUtils.toString(JSEvaluator.class.getResourceAsStream("init_global.js"), "UTF-8");
-                }
-                catch (final IOException ioe) {
-                    throw new UncheckedIOException("Failed to load init_global.js from classpath", ioe);
+    private void copyJavascriptGlobalsToScxmlContext(final Bindings global, final JSContext jsContext) {
+        if (global != null) {
+            for (final String key : global.keySet()) {
+                if (!SCXML_SYSTEM_CONTEXT.equals(key)) {
+                    jsContext.set(key, global.get(key));
                 }
             }
         }
     }
 
-    /**
-     * Gets the singleton ScriptEngine, initializing it on first access
-     * @return The ScriptEngine
-     */
-    protected ScriptEngine getEngine() {
-        if (engine == null) {
-            initEngine();
-        }
-        return engine;
-    }
-
-    /**
-     * Gets the current ScriptContext or create a new one.
-     * <p>
-     * The ScriptContext is (to be) shared across invocations for the same SCXML instance as it holds the Javascript 'global'
-     * context.
-     * </p>
-     * <p>
-     * The ScriptContext is using a {@link ScriptContext#ENGINE_SCOPE} as provided by the engine, which in case of Nashorn
-     * is bound to the Javscript global context. Note: do <em>not</em> confuse this with the {@link ScriptContext#GLOBAL_SCOPE} binding.
-     * </p>
-     * <p>For a newly created ScriptContext (and thus a new Javascript global context), the Javascript global context is
-     * initialized with the required and protected SCXML system variables and builtin In() operator via the
-     * <code>init_global.js</code> script, loaded as classpath resource.</p>
-     * <p>
-     * The SCXML system variables are bound as <code>"_scxmlSystemContext"</code> variable in the ENGINE_SCOPE
-     * as needed for the <code>init_global.js</code> script in the global context.
-     * This variable is bound to the ENGINE_SCOPE to ensure it cannot be 'shadowed' by an overriding variable assignment.
-     * </p>
-     * <p>
-     * The provided SCXML Context variables are bound via the GLOBAL_SCOPE using a {@link JSBindings} wrapper for each
-     * invocation.
-     * </p>
-     * <p>
-     * As the GLOBAL_SCOPE SCXML context variables <em>can</em> be overridden, which will result in new 'shadow'
-     * variables in the ENGINE_SCOPE, as well as new variables can be added to the ENGINE_SCOPE during script evaluation,
-     * after script execution all ENGINE_SCOPE variables (except the <code>"_scxmlSystemContext"</code> variable) must be
-     * copied/merged into the SCXML context to synchronize the SCXML context.
-     * </p>
-     * @param jsContext The current SCXML context
-     * @return The SCXML instance shared ScriptContext
-     * @throws ScriptException Thrown if the initialization of the Global Javascript engine itself failed
-     */
-    protected ScriptContext getScriptContext(final JSContext jsContext) throws ScriptException {
-        if (scriptContext == null) {
-            scriptContext = new SimpleScriptContext();
-            scriptContext.setBindings(getEngine().createBindings(), ScriptContext.ENGINE_SCOPE);
-            scriptContext.setBindings(new JSBindings(jsContext), ScriptContext.GLOBAL_SCOPE);
-            scriptContext.getBindings(ScriptContext.ENGINE_SCOPE).put(SCXML_SYSTEM_CONTEXT, jsContext.getSystemContext().getVars());
-            getEngine().eval(initGlobalsScript, scriptContext);
-        }
-        else {
-            // ensure updated / replaced SystemContext is used (like after SCXML instance go/reset)
-            scriptContext.getBindings(ScriptContext.ENGINE_SCOPE).put(SCXML_SYSTEM_CONTEXT, jsContext.getSystemContext().getVars());
-            ((JSBindings)scriptContext.getBindings(ScriptContext.GLOBAL_SCOPE)).setContext(jsContext);
-        }
-        return scriptContext;
-    }
-
-    @Override
-    public String getSupportedDatamodel() {
-        return SUPPORTED_DATA_MODEL;
-    }
-
-    /**
-     * Javascript engine semantics, using a retained global state, requires global SCXML context execution
-     * @return true
-     */
-    @Override
-    public boolean requiresGlobalContext() {
-        return true;
-    }
-
-    /**
-     * Creates a child context.
-     *
-     * @return Returns a new child JSContext.
-     *
-     */
-    @Override
-    public Context newContext(final Context parent) {
-        return new JSContext(parent);
-    }
-
     /**
      * Evaluates a Javascript expression using an SCXML instance shared {@link #getScriptContext(JSContext)}.
      * <p>
@@ -275,18 +186,87 @@ public class JSEvaluator extends AbstractBaseEvaluator {
     }
 
     /**
-     * Copy the Javscript global context (i.e. nashorn Global instance) variables to SCXML {@code jsContext}
-     * in order to make sure all the new global variables set by the JavaScript engine after evaluation are
-     * available from {@link JSContext} instance as well.
-     * <p>Note: the internal <code>"_scxmlSystemContext</code> variable is always skipped.</p>
-     * @param global The Javascript Bindings holding the Javascript Global context variables
-     * @param jsContext The SCXML context to copy/merge the variables into
+     * Gets the singleton ScriptEngine, initializing it on first access
+     * @return The ScriptEngine
      */
-    private void copyJavascriptGlobalsToScxmlContext(final Bindings global, final JSContext jsContext) {
-        if (global != null) {
-            for (final String key : global.keySet()) {
-                if (!SCXML_SYSTEM_CONTEXT.equals(key)) {
-                    jsContext.set(key, global.get(key));
+    protected ScriptEngine getEngine() {
+        if (engine == null) {
+            initEngine();
+        }
+        return engine;
+    }
+
+    /**
+     * Gets the current ScriptContext or create a new one.
+     * <p>
+     * The ScriptContext is (to be) shared across invocations for the same SCXML instance as it holds the Javascript 'global'
+     * context.
+     * </p>
+     * <p>
+     * The ScriptContext is using a {@link ScriptContext#ENGINE_SCOPE} as provided by the engine, which in case of Nashorn
+     * is bound to the Javscript global context. Note: do <em>not</em> confuse this with the {@link ScriptContext#GLOBAL_SCOPE} binding.
+     * </p>
+     * <p>For a newly created ScriptContext (and thus a new Javascript global context), the Javascript global context is
+     * initialized with the required and protected SCXML system variables and builtin In() operator via the
+     * <code>init_global.js</code> script, loaded as classpath resource.</p>
+     * <p>
+     * The SCXML system variables are bound as <code>"_scxmlSystemContext"</code> variable in the ENGINE_SCOPE
+     * as needed for the <code>init_global.js</code> script in the global context.
+     * This variable is bound to the ENGINE_SCOPE to ensure it cannot be 'shadowed' by an overriding variable assignment.
+     * </p>
+     * <p>
+     * The provided SCXML Context variables are bound via the GLOBAL_SCOPE using a {@link JSBindings} wrapper for each
+     * invocation.
+     * </p>
+     * <p>
+     * As the GLOBAL_SCOPE SCXML context variables <em>can</em> be overridden, which will result in new 'shadow'
+     * variables in the ENGINE_SCOPE, as well as new variables can be added to the ENGINE_SCOPE during script evaluation,
+     * after script execution all ENGINE_SCOPE variables (except the <code>"_scxmlSystemContext"</code> variable) must be
+     * copied/merged into the SCXML context to synchronize the SCXML context.
+     * </p>
+     * @param jsContext The current SCXML context
+     * @return The SCXML instance shared ScriptContext
+     * @throws ScriptException Thrown if the initialization of the Global Javascript engine itself failed
+     */
+    protected ScriptContext getScriptContext(final JSContext jsContext) throws ScriptException {
+        if (scriptContext == null) {
+            scriptContext = new SimpleScriptContext();
+            scriptContext.setBindings(getEngine().createBindings(), ScriptContext.ENGINE_SCOPE);
+            scriptContext.setBindings(new JSBindings(jsContext), ScriptContext.GLOBAL_SCOPE);
+            scriptContext.getBindings(ScriptContext.ENGINE_SCOPE).put(SCXML_SYSTEM_CONTEXT, jsContext.getSystemContext().getVars());
+            getEngine().eval(initGlobalsScript, scriptContext);
+        }
+        else {
+            // ensure updated / replaced SystemContext is used (like after SCXML instance go/reset)
+            scriptContext.getBindings(ScriptContext.ENGINE_SCOPE).put(SCXML_SYSTEM_CONTEXT, jsContext.getSystemContext().getVars());
+            ((JSBindings)scriptContext.getBindings(ScriptContext.GLOBAL_SCOPE)).setContext(jsContext);
+        }
+        return scriptContext;
+    }
+
+    @Override
+    public String getSupportedDatamodel() {
+        return SUPPORTED_DATA_MODEL;
+    }
+
+    /**
+     * Initialize the singleton Javascript ScriptEngine to be used with a separate ScriptContext for each SCXML instance
+     * not sharing their global scope, see {@link #getScriptContext(JSContext)}.
+     * <p>
+     * The SCXML required protected system variables and (possible) other Javascript global initializations are defined
+     * in a <code>init_global.js</code> script which is pre-loaded as (classpath) resource, to be executed once during
+     * initialization of a new Javascript (Nashorn) Global.
+     * </p>
+     */
+    protected synchronized void initEngine() {
+        if (engine == null) {
+            engine = new ScriptEngineManager().getEngineByName("JavaScript");
+            if (initGlobalsScript == null) {
+                try {
+                    initGlobalsScript = IOUtils.toString(JSEvaluator.class.getResourceAsStream("init_global.js"), "UTF-8");
+                }
+                catch (final IOException ioe) {
+                    throw new UncheckedIOException("Failed to load init_global.js from classpath", ioe);
                 }
             }
         }
@@ -307,4 +287,24 @@ public class JSEvaluator extends AbstractBaseEvaluator {
             ctx.setLocal(id, eval(ctx, "Java.from("+id+")"));
         }
     }
+
+    /**
+     * Creates a child context.
+     *
+     * @return Returns a new child JSContext.
+     *
+     */
+    @Override
+    public Context newContext(final Context parent) {
+        return new JSContext(parent);
+    }
+
+    /**
+     * Javascript engine semantics, using a retained global state, requires global SCXML context execution
+     * @return true
+     */
+    @Override
+    public boolean requiresGlobalContext() {
+        return true;
+    }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlContext.java b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlContext.java
index bbb4db43..34989aaa 100644
--- a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlContext.java
+++ b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlContext.java
@@ -36,15 +36,6 @@ public class JexlContext extends SimpleContext
     public JexlContext() {
     }
 
-    /**
-     * Constructor with initial vars.
-     * @param parent The parent context
-     * @param initialVars The initial set of variables.
-     */
-    public JexlContext(final Context parent, final Map<String, Object> initialVars) {
-        super(parent, initialVars);
-    }
-
     /**
      * Constructor with parent context.
      *
@@ -53,5 +44,14 @@ public class JexlContext extends SimpleContext
     public JexlContext(final Context parent) {
         super(parent);
     }
+
+    /**
+     * Constructor with initial vars.
+     * @param parent The parent context
+     * @param initialVars The initial set of variables.
+     */
+    public JexlContext(final Context parent, final Map<String, Object> initialVars) {
+        super(parent, initialVars);
+    }
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java
index 643e3736..a490ba16 100644
--- a/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java
+++ b/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java
@@ -42,18 +42,8 @@ import org.apache.commons.scxml2.model.SCXML;
  */
 public class JexlEvaluator extends AbstractBaseEvaluator {
 
-    /** Serial version UID. */
-    private static final long serialVersionUID = 1L;
-
-    public static final String SUPPORTED_DATA_MODEL = "jexl";
-
     public static class JexlEvaluatorProvider implements EvaluatorProvider {
 
-        @Override
-        public String getSupportedDatamodel() {
-            return SUPPORTED_DATA_MODEL;
-        }
-
         @Override
         public Evaluator getEvaluator() {
             return new JexlEvaluator();
@@ -63,8 +53,18 @@ public class JexlEvaluator extends AbstractBaseEvaluator {
         public Evaluator getEvaluator(final SCXML document) {
             return new JexlEvaluator();
         }
+
+        @Override
+        public String getSupportedDatamodel() {
+            return SUPPORTED_DATA_MODEL;
+        }
     }
 
+    /** Serial version UID. */
+    private static final long serialVersionUID = 1L;
+
+    public static final String SUPPORTED_DATA_MODEL = "jexl";
+
     /** Error message if evaluation context is not a JexlContext. */
     private static final String ERR_CTX_TYPE = "Error evaluating JEXL "
         + "expression, Context must be a org.apache.commons.scxml2.env.jexl.JexlContext";
@@ -79,14 +79,19 @@ public class JexlEvaluator extends AbstractBaseEvaluator {
         jexlEngine = getJexlEngine();
     }
 
-    @Override
-    public String getSupportedDatamodel() {
-        return SUPPORTED_DATA_MODEL;
-    }
-
-    @Override
-    public boolean requiresGlobalContext() {
-        return false;
+    /**
+     * Create the internal JexlEngine member during the initialization.
+     * This method can be overriden to specify more detailed options
+     * into the JexlEngine.
+     * @return new JexlEngine instance
+     */
+    protected JexlEngine createJexlEngine() {
+        // With null prefix, define top-level user-defined functions.
+        // See javadoc of org.apache.commons.jexl2.JexlEngine#setFunctions(Map<String,Object> funcs) for detail.
+        final Map<String, Object> funcs = new HashMap<>();
+        funcs.put(null, JexlBuiltin.class);
+        JexlPermissions permissions = JexlPermissions.RESTRICTED.compose("org.apache.commons.scxml2.*");
+        return new JexlBuilder().permissions(permissions).namespaces(funcs).cache(256).create();
     }
 
     /**
@@ -163,30 +168,16 @@ public class JexlEvaluator extends AbstractBaseEvaluator {
     }
 
     /**
-     * Create a new child context.
+     * Create a new context which is the summation of contexts from the
+     * current state to document root, child has priority over parent
+     * in scoping rules.
      *
-     * @param parent parent context
-     * @return new child context
-     * @see Evaluator#newContext(Context)
-     */
-    @Override
-    public Context newContext(final Context parent) {
-        return new JexlContext(parent);
-    }
-
-    /**
-     * Create the internal JexlEngine member during the initialization.
-     * This method can be overriden to specify more detailed options
-     * into the JexlEngine.
-     * @return new JexlEngine instance
+     * @param nodeCtx The JexlContext for this state.
+     * @return The effective JexlContext for the path leading up to
+     *         document root.
      */
-    protected JexlEngine createJexlEngine() {
-        // With null prefix, define top-level user-defined functions.
-        // See javadoc of org.apache.commons.jexl2.JexlEngine#setFunctions(Map<String,Object> funcs) for detail.
-        final Map<String, Object> funcs = new HashMap<>();
-        funcs.put(null, JexlBuiltin.class);
-        JexlPermissions permissions = JexlPermissions.RESTRICTED.compose("org.apache.commons.scxml2.*");
-        return new JexlBuilder().permissions(permissions).namespaces(funcs).cache(256).create();
+    protected JexlContext getEffectiveContext(final JexlContext nodeCtx) {
+        return new JexlContext(nodeCtx, new EffectiveContextMap(nodeCtx));
     }
 
     /**
@@ -210,17 +201,26 @@ public class JexlEvaluator extends AbstractBaseEvaluator {
         return engine;
     }
 
+    @Override
+    public String getSupportedDatamodel() {
+        return SUPPORTED_DATA_MODEL;
+    }
+
     /**
-     * Create a new context which is the summation of contexts from the
-     * current state to document root, child has priority over parent
-     * in scoping rules.
+     * Create a new child context.
      *
-     * @param nodeCtx The JexlContext for this state.
-     * @return The effective JexlContext for the path leading up to
-     *         document root.
+     * @param parent parent context
+     * @return new child context
+     * @see Evaluator#newContext(Context)
      */
-    protected JexlContext getEffectiveContext(final JexlContext nodeCtx) {
-        return new JexlContext(nodeCtx, new EffectiveContextMap(nodeCtx));
+    @Override
+    public Context newContext(final Context parent) {
+        return new JexlContext(parent);
+    }
+
+    @Override
+    public boolean requiresGlobalContext() {
+        return false;
     }
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/env/minimal/MinimalContext.java b/src/main/java/org/apache/commons/scxml2/env/minimal/MinimalContext.java
index 23af0de4..84e017f7 100644
--- a/src/main/java/org/apache/commons/scxml2/env/minimal/MinimalContext.java
+++ b/src/main/java/org/apache/commons/scxml2/env/minimal/MinimalContext.java
@@ -54,12 +54,13 @@ public class MinimalContext extends SimpleContext {
     }
 
     @Override
-    public void set(final String name, final Object value) {
+    public Object get(final String name) {
+        return null;
     }
 
     @Override
-    public Object get(final String name) {
-        return null;
+    public Map<String, Object> getVars() {
+        return Collections.emptyMap();
     }
 
     @Override
@@ -77,15 +78,14 @@ public class MinimalContext extends SimpleContext {
     }
 
     @Override
-    public void setLocal(final String name, final Object value) {
+    public void set(final String name, final Object value) {
     }
 
     @Override
-    protected void setVars(final Map<String, Object> vars) {
+    public void setLocal(final String name, final Object value) {
     }
 
     @Override
-    public Map<String, Object> getVars() {
-        return Collections.emptyMap();
+    protected void setVars(final Map<String, Object> vars) {
     }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/env/minimal/MinimalEvaluator.java b/src/main/java/org/apache/commons/scxml2/env/minimal/MinimalEvaluator.java
index 93adb094..c1846649 100644
--- a/src/main/java/org/apache/commons/scxml2/env/minimal/MinimalEvaluator.java
+++ b/src/main/java/org/apache/commons/scxml2/env/minimal/MinimalEvaluator.java
@@ -33,18 +33,8 @@ import org.apache.commons.scxml2.model.SCXML;
  */
 public class MinimalEvaluator implements Evaluator, Serializable {
 
-    /** Serial version UID. */
-    private static final long serialVersionUID = 1L;
-
-    public static final String SUPPORTED_DATA_MODEL = Evaluator.NULL_DATA_MODEL;
-
     public static class MinimalEvaluatorProvider implements EvaluatorProvider {
 
-        @Override
-        public String getSupportedDatamodel() {
-            return SUPPORTED_DATA_MODEL;
-        }
-
         @Override
         public Evaluator getEvaluator() {
             return new MinimalEvaluator();
@@ -54,17 +44,17 @@ public class MinimalEvaluator implements Evaluator, Serializable {
         public Evaluator getEvaluator(final SCXML document) {
             return new MinimalEvaluator();
         }
-    }
 
-    @Override
-    public String getSupportedDatamodel() {
-        return SUPPORTED_DATA_MODEL;
+        @Override
+        public String getSupportedDatamodel() {
+            return SUPPORTED_DATA_MODEL;
+        }
     }
 
-    @Override
-    public boolean requiresGlobalContext() {
-        return true;
-    }
+    /** Serial version UID. */
+    private static final long serialVersionUID = 1L;
+
+    public static final String SUPPORTED_DATA_MODEL = Evaluator.NULL_DATA_MODEL;
 
     @Override
     public Object cloneData(final Object data) {
@@ -76,6 +66,11 @@ public class MinimalEvaluator implements Evaluator, Serializable {
         return expr;
     }
 
+    @Override
+    public void evalAssign(final Context ctx, final String location, final Object data) throws SCXMLExpressionException {
+        throw new UnsupportedOperationException("Assign expressions are not supported by the \"null\" datamodel");
+    }
+
     @Override
     public Boolean evalCond(final Context ctx, final String expr) throws SCXMLExpressionException {
         // only support the "In(stateId)" predicate
@@ -88,17 +83,22 @@ public class MinimalEvaluator implements Evaluator, Serializable {
     }
 
     @Override
-    public void evalAssign(final Context ctx, final String location, final Object data) throws SCXMLExpressionException {
-        throw new UnsupportedOperationException("Assign expressions are not supported by the \"null\" datamodel");
+    public Object evalScript(final Context ctx, final String script) throws SCXMLExpressionException {
+        throw new UnsupportedOperationException("Scripts are not supported by the \"null\" datamodel");
     }
 
     @Override
-    public Object evalScript(final Context ctx, final String script) throws SCXMLExpressionException {
-        throw new UnsupportedOperationException("Scripts are not supported by the \"null\" datamodel");
+    public String getSupportedDatamodel() {
+        return SUPPORTED_DATA_MODEL;
     }
 
     @Override
     public Context newContext(final Context parent) {
         return parent instanceof MinimalContext ? parent : new MinimalContext(parent);
     }
+
+    @Override
+    public boolean requiresGlobalContext() {
+        return true;
+    }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/invoke/Invoker.java b/src/main/java/org/apache/commons/scxml2/invoke/Invoker.java
index c8dd791b..43462f44 100644
--- a/src/main/java/org/apache/commons/scxml2/invoke/Invoker.java
+++ b/src/main/java/org/apache/commons/scxml2/invoke/Invoker.java
@@ -67,25 +67,13 @@ import org.apache.commons.scxml2.TriggerEvent;
 public interface Invoker {
 
     /**
-     * @return get the invoke ID provided by the parent state machine executor
-     */
-    String getInvokeId();
-
-    /**
-     * Sets the invoke ID provided by the parent state machine executor
-     * Implementations must use this ID for constructing the event name for
-     * the special "done" event (and optionally, for other event names
-     * as well).
+     * Cancel this invocation.
      *
-     * @param invokeId The invoke ID provided by the parent state machine executor.
-     */
-    void setInvokeId(String invokeId);
-
-    /**
-     * Sets the parent SCXMLExecutor through which this Invoker is initiated
-     * @param scxmlExecutor the parent SCXMLExecutor
+     * @throws InvokerException In case there is a fatal problem with
+     *                          canceling this invoke.
      */
-    void setParentSCXMLExecutor(SCXMLExecutor scxmlExecutor);
+    void cancel()
+    throws InvokerException;
 
     /**
      * Gets the child IO Processor to register for communication with
@@ -95,6 +83,11 @@ public interface Invoker {
      */
     SCXMLIOProcessor getChildIOProcessor();
 
+    /**
+     * @return get the invoke ID provided by the parent state machine executor
+     */
+    String getInvokeId();
+
     /**
      * Invoke the SCXML document located at an external URL.
      *
@@ -133,12 +126,19 @@ public interface Invoker {
     throws InvokerException;
 
     /**
-     * Cancel this invocation.
+     * Sets the invoke ID provided by the parent state machine executor
+     * Implementations must use this ID for constructing the event name for
+     * the special "done" event (and optionally, for other event names
+     * as well).
      *
-     * @throws InvokerException In case there is a fatal problem with
-     *                          canceling this invoke.
+     * @param invokeId The invoke ID provided by the parent state machine executor.
      */
-    void cancel()
-    throws InvokerException;
+    void setInvokeId(String invokeId);
+
+    /**
+     * Sets the parent SCXMLExecutor through which this Invoker is initiated
+     * @param scxmlExecutor the parent SCXMLExecutor
+     */
+    void setParentSCXMLExecutor(SCXMLExecutor scxmlExecutor);
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/invoke/InvokerException.java b/src/main/java/org/apache/commons/scxml2/invoke/InvokerException.java
index 066dad41..194a17b3 100644
--- a/src/main/java/org/apache/commons/scxml2/invoke/InvokerException.java
+++ b/src/main/java/org/apache/commons/scxml2/invoke/InvokerException.java
@@ -35,14 +35,6 @@ public class InvokerException extends Exception {
         super(message);
     }
 
-    /**
-     * @see Exception#Exception(Throwable)
-     * @param cause The cause
-     */
-    public InvokerException(final Throwable cause) {
-        super(cause);
-    }
-
     /**
      * @see Exception#Exception(String, Throwable)
      * @param message The error message
@@ -53,5 +45,13 @@ public class InvokerException extends Exception {
         super(message, cause);
     }
 
+    /**
+     * @see Exception#Exception(Throwable)
+     * @param cause The cause
+     */
+    public InvokerException(final Throwable cause) {
+        super(cause);
+    }
+
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/invoke/SimpleSCXMLInvoker.java b/src/main/java/org/apache/commons/scxml2/invoke/SimpleSCXMLInvoker.java
index 0d12f820..0193c496 100644
--- a/src/main/java/org/apache/commons/scxml2/invoke/SimpleSCXMLInvoker.java
+++ b/src/main/java/org/apache/commons/scxml2/invoke/SimpleSCXMLInvoker.java
@@ -55,34 +55,43 @@ public class SimpleSCXMLInvoker implements Invoker, Serializable {
      * {@inheritDoc}.
      */
     @Override
-    public String getInvokeId() {
-        return invokeId;
+    public void cancel()
+    throws InvokerException {
+        cancelled = true;
+        executor.getParentSCXMLIOProcessor().close();
+        executor.addEvent(new EventBuilder("cancel.invoke."+ invokeId, TriggerEvent.CANCEL_EVENT).build());
     }
 
-    /**
-     * {@inheritDoc}.
-     */
-    @Override
-    public void setInvokeId(final String invokeId) {
-        this.invokeId = invokeId;
-        this.cancelled = false;
+    protected void execute(final SCXML scxml, final Map<String, Object> params) throws InvokerException {
+        try {
+            executor = new SCXMLExecutor(parentSCXMLExecutor, invokeId, scxml);
+        }
+        catch (final ModelException me) {
+            throw new InvokerException(me);
+        }
+        executor.addListener(scxml, new SimpleSCXMLListener());
+        try {
+            executor.run(params);
+        } catch (final ModelException me) {
+            throw new InvokerException(me.getMessage(), me.getCause());
+        }
     }
 
     /**
      * {@inheritDoc}.
      */
     @Override
-    public void setParentSCXMLExecutor(final SCXMLExecutor parentSCXMLExecutor) {
-        this.parentSCXMLExecutor = parentSCXMLExecutor;
+    public SCXMLIOProcessor getChildIOProcessor() {
+        // not used
+        return executor;
     }
 
     /**
      * {@inheritDoc}.
      */
     @Override
-    public SCXMLIOProcessor getChildIOProcessor() {
-        // not used
-        return executor;
+    public String getInvokeId() {
+        return invokeId;
     }
 
     /**
@@ -115,21 +124,6 @@ public class SimpleSCXMLInvoker implements Invoker, Serializable {
         execute(scxml, params);
     }
 
-    protected void execute(final SCXML scxml, final Map<String, Object> params) throws InvokerException {
-        try {
-            executor = new SCXMLExecutor(parentSCXMLExecutor, invokeId, scxml);
-        }
-        catch (final ModelException me) {
-            throw new InvokerException(me);
-        }
-        executor.addListener(scxml, new SimpleSCXMLListener());
-        try {
-            executor.run(params);
-        } catch (final ModelException me) {
-            throw new InvokerException(me.getMessage(), me.getCause());
-        }
-    }
-
     /**
      * {@inheritDoc}.
      */
@@ -145,11 +139,17 @@ public class SimpleSCXMLInvoker implements Invoker, Serializable {
      * {@inheritDoc}.
      */
     @Override
-    public void cancel()
-    throws InvokerException {
-        cancelled = true;
-        executor.getParentSCXMLIOProcessor().close();
-        executor.addEvent(new EventBuilder("cancel.invoke."+ invokeId, TriggerEvent.CANCEL_EVENT).build());
+    public void setInvokeId(final String invokeId) {
+        this.invokeId = invokeId;
+        this.cancelled = false;
+    }
+
+    /**
+     * {@inheritDoc}.
+     */
+    @Override
+    public void setParentSCXMLExecutor(final SCXMLExecutor parentSCXMLExecutor) {
+        this.parentSCXMLExecutor = parentSCXMLExecutor;
     }
 }
 
diff --git a/src/main/java/org/apache/commons/scxml2/io/ContentParser.java b/src/main/java/org/apache/commons/scxml2/io/ContentParser.java
index 3f9f45fe..87863b75 100644
--- a/src/main/java/org/apache/commons/scxml2/io/ContentParser.java
+++ b/src/main/java/org/apache/commons/scxml2/io/ContentParser.java
@@ -51,48 +51,31 @@ public class ContentParser {
     public static final ContentParser DEFAULT_PARSER = new ContentParser();
 
     /**
-     * Jackson JSON ObjectMapper
-     */
-    private final ObjectMapper jsonObjectMapper;
-
-    /**
-     * Default constructor initializing a Jackson ObjectMapper allowing embedded comments, including YAML style
+     * Check if content starts with JSON object '{' or array '[' marker
+     * @param content text to check
+     * @return true if content start with '{' or '[' character
      */
-    public ContentParser() {
-        this.jsonObjectMapper = new ObjectMapper();
-        jsonObjectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
-        jsonObjectMapper.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true);
+    public static boolean hasJsonSignature(final String content) {
+        final char c = !content.isEmpty() ? content.charAt(0) : 0;
+        return c == '{' || c == '[';
     }
 
     /**
-     * Constructor with a custom configured Jackson ObjectMapper
-     * @param jsonObjectMapper custom configured Jackson ObjectMapper
+     * Check if content indicates its an XML document
+     * @param content content to check
+     * @return true if content indicates its an XML document
      */
-    public ContentParser(final ObjectMapper jsonObjectMapper) {
-        this.jsonObjectMapper = jsonObjectMapper;
+    public static boolean hasXmlSignature(final String content) {
+        return content != null && content.startsWith("<?xml ");
     }
 
     /**
-     * Trim pre/post-fixed whitespace from content string
-     * @param content content to trim
-     * @return trimmed content
+     * Check if a character is whitespace (space, tab, newline, cr) or not
+     * @param c character to check
+     * @return true if character is whitespace
      */
-    public static String trimContent(final String content) {
-        if (content != null) {
-            int start = 0;
-            int length = content.length();
-            while (start < length && isWhiteSpace(content.charAt(start))) {
-                start++;
-            }
-            while (length > start && isWhiteSpace(content.charAt(length - 1))) {
-                length--;
-            }
-            if (start == length) {
-                return "";
-            }
-            return content.substring(start, length);
-        }
-        return null;
+    public static boolean isWhiteSpace(final char c) {
+        return c==' ' || c=='\n' || c=='\t' || c=='\r';
     }
 
     /**
@@ -129,31 +112,73 @@ public class ContentParser {
     }
 
     /**
-     * Check if a character is whitespace (space, tab, newline, cr) or not
-     * @param c character to check
-     * @return true if character is whitespace
+     * Trim pre/post-fixed whitespace from content string
+     * @param content content to trim
+     * @return trimmed content
      */
-    public static boolean isWhiteSpace(final char c) {
-        return c==' ' || c=='\n' || c=='\t' || c=='\r';
+    public static String trimContent(final String content) {
+        if (content != null) {
+            int start = 0;
+            int length = content.length();
+            while (start < length && isWhiteSpace(content.charAt(start))) {
+                start++;
+            }
+            while (length > start && isWhiteSpace(content.charAt(length - 1))) {
+                length--;
+            }
+            if (start == length) {
+                return "";
+            }
+            return content.substring(start, length);
+        }
+        return null;
     }
 
     /**
-     * Check if content starts with JSON object '{' or array '[' marker
-     * @param content text to check
-     * @return true if content start with '{' or '[' character
+     * Jackson JSON ObjectMapper
      */
-    public static boolean hasJsonSignature(final String content) {
-        final char c = !content.isEmpty() ? content.charAt(0) : 0;
-        return c == '{' || c == '[';
+    private final ObjectMapper jsonObjectMapper;
+
+    /**
+     * Default constructor initializing a Jackson ObjectMapper allowing embedded comments, including YAML style
+     */
+    public ContentParser() {
+        this.jsonObjectMapper = new ObjectMapper();
+        jsonObjectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
+        jsonObjectMapper.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true);
     }
 
     /**
-     * Check if content indicates its an XML document
-     * @param content content to check
-     * @return true if content indicates its an XML document
+     * Constructor with a custom configured Jackson ObjectMapper
+     * @param jsonObjectMapper custom configured Jackson ObjectMapper
      */
-    public static boolean hasXmlSignature(final String content) {
-        return content != null && content.startsWith("<?xml ");
+    public ContentParser(final ObjectMapper jsonObjectMapper) {
+        this.jsonObjectMapper = jsonObjectMapper;
+    }
+
+    /**
+     * Parse a string into a ParsedValue content object, following the SCXML rules as specified for the ECMAscript (section B.2.1) Data Model
+     * <ul>
+     *   <li>if the content can be interpreted as JSON, it will be parsed as JSON into an 'raw' object model</li>
+     *   <li>if the content can be interpreted as XML, it will be parsed into a XML DOM element</li>
+     *   <li>otherwise the content will be treated (cleaned) as a space-normalized string literal</li>
+     * </ul>
+     * @param content the content to parse
+     * @return the parsed content object
+     * @throws IOException In case of parsing exceptions
+     */
+    public ParsedValue parseContent(final String content) throws IOException {
+        if (content != null) {
+            final String src = trimContent(content);
+            if (hasJsonSignature(src)) {
+                return new JsonValue(parseJson(src), false);
+            }
+            if (hasXmlSignature(src)) {
+                return new NodeValue(parseXml(src));
+            }
+            return new TextValue(spaceNormalizeContent(src), false);
+        }
+        return null;
     }
 
     /**
@@ -167,13 +192,16 @@ public class ContentParser {
     }
 
     /**
-     * Transforms a jsonObject to a json String
-     * @param jsonObject object to transform
-     * @return json string
-     * @throws IOException if IO error occurs while serializing it to JSON
+     * Load a resource (URL) as an UTF-8 encoded content string to be parsed into a ParsedValue content object through {@link #parseContent(String)}
+     * @param resourceURL Resource URL to load content from
+     * @return the parsed content object
+     * @throws IOException In case of loading or parsing exceptions
      */
-    public String toJson(final Object jsonObject) throws IOException {
-        return jsonObjectMapper.writeValueAsString(jsonObject);
+    public ParsedValue parseResource(final String resourceURL) throws IOException {
+        try (InputStream in = new URL(resourceURL).openStream()) {
+            final String content = IOUtils.toString(in, "UTF-8");
+            return parseContent(content);
+        }
     }
 
     /**
@@ -192,6 +220,16 @@ public class ContentParser {
         return doc != null ? doc.getDocumentElement() : null;
     }
 
+    /**
+     * Transforms a jsonObject to a json String
+     * @param jsonObject object to transform
+     * @return json string
+     * @throws IOException if IO error occurs while serializing it to JSON
+     */
+    public String toJson(final Object jsonObject) throws IOException {
+        return jsonObjectMapper.writeValueAsString(jsonObject);
+    }
+
     /**
      * Transforms a XML Node to XML
      * @param node node to transform
@@ -213,42 +251,4 @@ public class ContentParser {
             throw new IOException(e);
         }
     }
-
-    /**
-     * Parse a string into a ParsedValue content object, following the SCXML rules as specified for the ECMAscript (section B.2.1) Data Model
-     * <ul>
-     *   <li>if the content can be interpreted as JSON, it will be parsed as JSON into an 'raw' object model</li>
-     *   <li>if the content can be interpreted as XML, it will be parsed into a XML DOM element</li>
-     *   <li>otherwise the content will be treated (cleaned) as a space-normalized string literal</li>
-     * </ul>
-     * @param content the content to parse
-     * @return the parsed content object
-     * @throws IOException In case of parsing exceptions
-     */
-    public ParsedValue parseContent(final String content) throws IOException {
-        if (content != null) {
-            final String src = trimContent(content);
-            if (hasJsonSignature(src)) {
-                return new JsonValue(parseJson(src), false);
-            }
-            if (hasXmlSignature(src)) {
-                return new NodeValue(parseXml(src));
-            }
-            return new TextValue(spaceNormalizeContent(src), false);
-        }
-        return null;
-    }
-
-    /**
-     * Load a resource (URL) as an UTF-8 encoded content string to be parsed into a ParsedValue content object through {@link #parseContent(String)}
-     * @param resourceURL Resource URL to load content from
-     * @return the parsed content object
-     * @throws IOException In case of loading or parsing exceptions
-     */
-    public ParsedValue parseResource(final String resourceURL) throws IOException {
-        try (InputStream in = new URL(resourceURL).openStream()) {
-            final String content = IOUtils.toString(in, "UTF-8");
-            return parseContent(content);
-        }
-    }
 }
diff --git a/src/main/java/org/apache/commons/scxml2/io/ModelUpdater.java b/src/main/java/org/apache/commons/scxml2/io/ModelUpdater.java
index 05eb23ed..caead5e0 100644
--- a/src/main/java/org/apache/commons/scxml2/io/ModelUpdater.java
+++ b/src/main/java/org/apache/commons/scxml2/io/ModelUpdater.java
@@ -117,50 +117,31 @@ final class ModelUpdater {
             + " must specify either one, but not both.";
 
     /**
-     * Discourage instantiation since this is a utility class.
-     */
-    private 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-read processing, and sets up the necessary
-     * object references throughtout the SCXML object model for the parsed
-     * document.</p>
+     * Gets a transition target identifier for error messages. This method is
+     * only called to produce an appropriate log message in some error
+     * conditions.
      *
-     * @param scxml The SCXML object (output from SCXMLReader)
-     * @throws ModelException If the object model is flawed
+     * @param tt The <code>TransitionTarget</code> object
+     * @return The transition target identifier for the error message
      */
-    static void updateSCXML(final SCXML scxml) throws ModelException {
-        initDocumentOrder(scxml.getChildren(), 1);
-
-        final String initial = scxml.getInitial();
-        final SimpleTransition initialTransition = new SimpleTransition();
-
-        if (initial != null) {
-
-            initialTransition.setNext(scxml.getInitial());
-            updateTransition(initialTransition, scxml.getTargets());
-
-            if (initialTransition.getTargets().isEmpty()) {
-                logAndThrowModelError(ERR_SCXML_NO_INIT, new Object[] {
-                        initial });
+    private static String getName(final TransitionTarget tt) {
+        String name = "anonymous transition target";
+        if (tt instanceof State) {
+            name = "anonymous state";
+            if (tt.getId() != null) {
+                name = "state with ID \"" + tt.getId() + "\"";
+            }
+        } else if (tt instanceof Parallel) {
+            name = "anonymous parallel";
+            if (tt.getId() != null) {
+                name = "parallel with ID \"" + tt.getId() + "\"";
             }
         } else {
-            // If 'initial' is not specified, the default initial state is
-            // the first child state in document order.
-            initialTransition.getTargets().add(scxml.getFirstChild());
+            if (tt.getId() != null) {
+                name = "transition target with ID \"" + tt.getId() + "\"";
+            }
         }
-
-        scxml.setInitialTransition(initialTransition);
-        final Map<String, TransitionTarget> targets = scxml.getTargets();
-        updateEnterableStates(scxml.getChildren(), targets);
-
-        scxml.getInitialTransition().setObservableId(1);
-        initObservables(scxml.getChildren(), 2);
+        return name;
     }
 
     /**
@@ -218,83 +199,20 @@ final class ModelUpdater {
     }
 
     /**
-     * Update this State object (part of post-read processing).
-     * Also checks for any errors in the document.
-     *
-     * @param state 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 state, final Map<String, TransitionTarget> targets)
-            throws ModelException {
-        final List<EnterableState> children = state.getChildren();
-        if (state.isComposite()) {
-            //initialize next / initial
-            Initial ini = state.getInitial();
-            if (ini == null) {
-                state.setFirst(children.get(0).getId());
-                ini = state.getInitial();
-            }
-            final SimpleTransition initialTransition = ini.getTransition();
-            updateTransition(initialTransition, targets);
-            final Set<TransitionTarget> initialStates = initialTransition.getTargets();
-            // we have to allow for an indirect descendant initial (targets)
-            //check that initialState is a descendant of s
-            if (initialStates.isEmpty()) {
-                logAndThrowModelError(ERR_STATE_BAD_INIT,
-                        new Object[] {getName(state)});
-            } else {
-                for (final TransitionTarget initialState : initialStates) {
-                    if (!initialState.isDescendantOf(state)) {
-                        logAndThrowModelError(ERR_STATE_BAD_INIT,
-                                new Object[] {getName(state)});
-                    }
-                }
-            }
-        }
-        else if (state.getInitial() != null) {
-            logAndThrowModelError(ERR_UNSUPPORTED_INIT, new Object[] {getName(state)});
-        }
-
-        final List<History> histories = state.getHistory();
-        if (!histories.isEmpty() && state.isSimple()) {
-            logAndThrowModelError(ERR_HISTORY_SIMPLE_STATE,
-                    new Object[] {getName(state)});
-        }
-        for (final History history : histories) {
-            updateHistory(history, targets, state);
-        }
-        for (final Transition transition : state.getTransitionsList()) {
-            updateTransition(transition, targets);
-        }
-
-        for (final Invoke inv : state.getInvokes()) {
-            if (inv.getSrc() != null && inv.getSrcexpr() != null) {
-                logAndThrowModelError(ERR_INVOKE_AMBIGUOUS_SRC, new Object[] {getName(state)});
-            }
-        }
-
-        updateEnterableStates(children, targets);
-    }
-
-    /**
-     * Update this Parallel object (part of post-read processing).
+     * Log an error discovered in post-read processing.
      *
-     * @param parallel The Parallel object
-     * @param targets The global Map of all transition targets
-     * @throws ModelException If the object model is flawed
+     * @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 updateParallel(final Parallel parallel, final Map<String, TransitionTarget> targets)
-            throws ModelException {
-        updateEnterableStates(parallel.getChildren(), targets);
-        for (final Transition transition : parallel.getTransitionsList()) {
-            updateTransition(transition, targets);
-        }
-        final List<History> histories = parallel.getHistory();
-        for (final History history : histories) {
-            updateHistory(history, targets, parallel);
-        }
-        // TODO: parallel must may have invokes too
+    private static void logAndThrowModelError(final String errType,
+                                              final Object[] msgArgs) throws ModelException {
+        final MessageFormat msgFormat = new MessageFormat(errType);
+        final String errMsg = msgFormat.format(msgArgs);
+        final org.apache.commons.logging.Log log = LogFactory.
+                getLog(ModelUpdater.class);
+        log.error(errMsg);
+        throw new ModelException(errMsg);
     }
 
     /**
@@ -358,6 +276,127 @@ final class ModelUpdater {
         }
     }
 
+    /**
+     * Update this Parallel object (part of post-read processing).
+     *
+     * @param parallel 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 parallel, final Map<String, TransitionTarget> targets)
+            throws ModelException {
+        updateEnterableStates(parallel.getChildren(), targets);
+        for (final Transition transition : parallel.getTransitionsList()) {
+            updateTransition(transition, targets);
+        }
+        final List<History> histories = parallel.getHistory();
+        for (final History history : histories) {
+            updateHistory(history, targets, parallel);
+        }
+        // TODO: parallel must may have invokes too
+    }
+
+    /*
+     * 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-read 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 SCXMLReader)
+     * @throws ModelException If the object model is flawed
+     */
+    static void updateSCXML(final SCXML scxml) throws ModelException {
+        initDocumentOrder(scxml.getChildren(), 1);
+
+        final String initial = scxml.getInitial();
+        final SimpleTransition initialTransition = new SimpleTransition();
+
+        if (initial != null) {
+
+            initialTransition.setNext(scxml.getInitial());
+            updateTransition(initialTransition, scxml.getTargets());
+
+            if (initialTransition.getTargets().isEmpty()) {
+                logAndThrowModelError(ERR_SCXML_NO_INIT, new Object[] {
+                        initial });
+            }
+        } else {
+            // If 'initial' is not specified, the default initial state is
+            // the first child state in document order.
+            initialTransition.getTargets().add(scxml.getFirstChild());
+        }
+
+        scxml.setInitialTransition(initialTransition);
+        final Map<String, TransitionTarget> targets = scxml.getTargets();
+        updateEnterableStates(scxml.getChildren(), targets);
+
+        scxml.getInitialTransition().setObservableId(1);
+        initObservables(scxml.getChildren(), 2);
+    }
+
+    /**
+     * Update this State object (part of post-read processing).
+     * Also checks for any errors in the document.
+     *
+     * @param state 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 state, final Map<String, TransitionTarget> targets)
+            throws ModelException {
+        final List<EnterableState> children = state.getChildren();
+        if (state.isComposite()) {
+            //initialize next / initial
+            Initial ini = state.getInitial();
+            if (ini == null) {
+                state.setFirst(children.get(0).getId());
+                ini = state.getInitial();
+            }
+            final SimpleTransition initialTransition = ini.getTransition();
+            updateTransition(initialTransition, targets);
+            final Set<TransitionTarget> initialStates = initialTransition.getTargets();
+            // we have to allow for an indirect descendant initial (targets)
+            //check that initialState is a descendant of s
+            if (initialStates.isEmpty()) {
+                logAndThrowModelError(ERR_STATE_BAD_INIT,
+                        new Object[] {getName(state)});
+            } else {
+                for (final TransitionTarget initialState : initialStates) {
+                    if (!initialState.isDescendantOf(state)) {
+                        logAndThrowModelError(ERR_STATE_BAD_INIT,
+                                new Object[] {getName(state)});
+                    }
+                }
+            }
+        }
+        else if (state.getInitial() != null) {
+            logAndThrowModelError(ERR_UNSUPPORTED_INIT, new Object[] {getName(state)});
+        }
+
+        final List<History> histories = state.getHistory();
+        if (!histories.isEmpty() && state.isSimple()) {
+            logAndThrowModelError(ERR_HISTORY_SIMPLE_STATE,
+                    new Object[] {getName(state)});
+        }
+        for (final History history : histories) {
+            updateHistory(history, targets, state);
+        }
+        for (final Transition transition : state.getTransitionsList()) {
+            updateTransition(transition, targets);
+        }
+
+        for (final Invoke inv : state.getInvokes()) {
+            if (inv.getSrc() != null && inv.getSrcexpr() != null) {
+                logAndThrowModelError(ERR_INVOKE_AMBIGUOUS_SRC, new Object[] {getName(state)});
+            }
+        }
+
+        updateEnterableStates(children, targets);
+    }
+
     /**
      * Update this Transition object (part of post-read processing).
      *
@@ -394,51 +433,6 @@ final class ModelUpdater {
         }
     }
 
-    /**
-     * Log an error discovered in post-read 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 {
-        final MessageFormat msgFormat = new MessageFormat(errType);
-        final String errMsg = msgFormat.format(msgArgs);
-        final org.apache.commons.logging.Log log = LogFactory.
-                getLog(ModelUpdater.class);
-        log.error(errMsg);
-        throw new ModelException(errMsg);
-    }
-
-    /**
-     * Gets a transition target identifier for error messages. This method is
-     * only called to produce an appropriate log message in some error
-     * conditions.
-     *
-     * @param tt The <code>TransitionTarget</code> object
-     * @return The transition target identifier for the error message
-     */
-    private static String getName(final TransitionTarget tt) {
-        String name = "anonymous transition target";
-        if (tt instanceof State) {
-            name = "anonymous state";
-            if (tt.getId() != null) {
-                name = "state with ID \"" + tt.getId() + "\"";
-            }
-        } else if (tt instanceof Parallel) {
-            name = "anonymous parallel";
-            if (tt.getId() != null) {
-                name = "parallel with ID \"" + tt.getId() + "\"";
-            }
-        } else {
-            if (tt.getId() != null) {
-                name = "transition target with ID \"" + tt.getId() + "\"";
-            }
-        }
-        return name;
-    }
-
     /**
      * If a transition has multiple targets, then they satisfy the following
      * criteria:
@@ -482,4 +476,10 @@ final class ModelUpdater {
         // least common ancestor must be a parallel
         return first != null && i > 0 && first.getAncestor(i-1) instanceof Parallel;
    }
+
+    /**
+     * Discourage instantiation since this is a utility class.
+     */
+    private ModelUpdater() {
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java b/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java
index b358787d..21dfe9bc 100644
--- a/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java
+++ b/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java
@@ -118,1344 +118,1139 @@ import org.xml.sax.SAXException;
  */
 public final class SCXMLReader {
 
-    private static final org.apache.commons.logging.Log logger = LogFactory.getLog(SCXMLReader.class);
-
-    /**
-     * By default Sun/Oracle XMLStreamReader implementation doesn't report CDATA events.
-     * This can be turned on (as needed by SCXMLReader) by setting this property TRUE
-     */
-    public final static String XMLInputFactory_JDK_PROP_REPORT_CDATA = "http://java.sun.com/xml/stream/properties/report-cdata-event";
-
-    //---------------------- PRIVATE CONSTANTS ----------------------//
-    /**
-     * The version attribute value the SCXML element <em>must</em> have as stated by the spec: 3.2.1
-     */
-    private static final String SCXML_REQUIRED_VERSION = "1.0";
-    /**
-     * The default namespace for attributes.
-     */
-    private static final String XMLNS_DEFAULT = null;
-
-    //---- ERROR MESSAGES ----//
+    //------------------------- CONFIGURATION CLASS -------------------------//
     /**
-     * Null URL passed as argument.
+     * <p>
+     * Configuration for the {@link SCXMLReader}. The configuration properties necessary for the following are
+     * covered:
+     * </p>
+     *
+     * <ul>
+     *   <li>{@link XMLInputFactory} configuration properties such as {@link XMLReporter}, {@link XMLResolver} and
+     *   {@link XMLEventAllocator}</li>
+     *   <li>{@link XMLStreamReader} configuration properties such as <code>systemId</code> and <code>encoding</code>
+     *   </li>
+     *   <li>Commons SCXML object model configuration properties such as the list of custom actions and the
+     *   {@link PathResolver} to use.</li>
+     * </ul>
      */
-    private static final String ERR_NULL_URL = "Cannot parse null URL";
+    public static class Configuration {
 
-    /**
-     * Null path passed as argument.
-     */
-    private static final String ERR_NULL_PATH = "Cannot parse null path";
+        /*
+         * Configuration properties for this {@link SCXMLReader}.
+         */
+        // XMLInputFactory configuration properties.
+        /**
+         * The <code>factoryId</code> to use for the {@link XMLInputFactory}.
+         */
+        final String factoryId;
 
-    /**
-     * Null InputStream passed as argument.
-     */
-    private static final String ERR_NULL_ISTR = "Cannot parse null InputStream";
+        /**
+         * The {@link ClassLoader} to use for the {@link XMLInputFactory} instance to create.
+         */
+        final ClassLoader factoryClassLoader;
 
-    /**
-     * Null Reader passed as argument.
-     */
-    private static final String ERR_NULL_READ = "Cannot parse null Reader";
+        /**
+         * The {@link XMLEventAllocator} for the {@link XMLInputFactory}.
+         */
+        final XMLEventAllocator allocator;
 
-    /**
-     * Null Source passed as argument.
-     */
-    private static final String ERR_NULL_SRC = "Cannot parse null Source";
+        /**
+         * The map of properties (keys are property name strings, values are object property values) for the
+         * {@link XMLInputFactory}.
+         */
+        final Map<String, Object> properties;
 
-    /**
-     * 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 actions list"
-            + " contained unknown object, class not a Commons SCXML Action class subtype: ";
+        /**
+         * The {@link XMLResolver} for the {@link XMLInputFactory}.
+         */
+        final XMLResolver resolver;
 
-    /**
-     * Parser configuration error while trying to parse stream to DOM node(s).
-     */
-    private static final String ERR_PARSER_CFG = "ParserConfigurationException while trying"
-            + " to parse stream into DOM node(s).";
+        /**
+         * The {@link XMLReporter} for the {@link XMLInputFactory}.
+         */
+        final XMLReporter reporter;
 
-    /**
-     * Error message when the URI in a &lt;state&gt;'s &quot;src&quot;
-     * attribute does not point to a valid SCXML document, and thus cannot be
-     * parsed.
-     */
-    private static final String ERR_STATE_SRC =
-            "Source attribute in <state src=\"{0}\"> cannot be parsed";
+        // XMLStreamReader configuration properties.
+        /**
+         * The <code>encoding</code> to use for the {@link XMLStreamReader}.
+         */
+        final String encoding;
 
-    /**
-     * Error message when the target of the URI fragment in a &lt;state&gt;'s
-     * &quot;src&quot; attribute is not defined in the referenced document.
-     */
-    private static final String ERR_STATE_SRC_FRAGMENT = "URI Fragment in "
-            + "<state src=\"{0}\"> is an unknown state in referenced document";
+        /**
+         * The <code>systemId</code> to use for the {@link XMLStreamReader}.
+         */
+        final String systemId;
 
-    /**
-     * Error message when the target of the URI fragment in a &lt;state&gt;'s
-     * &quot;src&quot; attribute is not a &lt;state&gt; or &lt;final&gt; in
-     * the referenced document.
-     */
-    private static final String ERR_STATE_SRC_FRAGMENT_TARGET = "URI Fragment"
-            + " in <state src=\"{0}\"> does not point to a <state> or <final>";
+        /**
+         * Whether to validate the input with the XML Schema for SCXML.
+         */
+        final boolean validate;
 
-    /**
-     * Error message when the target of the URI fragment in a &lt;state&gt;'s
-     * &quot;src&quot; attribute is not a &lt;state&gt; or &lt;final&gt; in
-     * the referenced document.
-     */
-    private static final String ERR_REQUIRED_ATTRIBUTE_MISSING = "<{0}> is missing"
-            +" required attribute \"{1}\" value at {2}";
+        // Commons SCXML object model configuration properties.
+        /**
+         * The list of Commons SCXML custom actions that will be available for this document.
+         */
+        final List<CustomAction> customActions;
 
-    /**
-     * Error message when the target of the URI fragment in a &lt;state&gt;'s
-     * &quot;src&quot; attribute is not a &lt;state&gt; or &lt;final&gt; in
-     * the referenced document.
-     */
-    private static final String ERR_ATTRIBUTE_NOT_BOOLEAN = "Illegal value \"{0}\""
-            + "for attribute \"{1}\" in element <{2}> at {3}."
-            +" Only the value \"true\" or \"false\" is allowed.";
+        /**
+         * The {@link ClassLoader} to use for loading the {@link CustomAction} instances to create.
+         */
+        final ClassLoader customActionClassLoader;
 
-    /**
-     * Error message when the element (state|parallel|final|history) uses an id value
-     * with the reserved prefix {@link SCXML#GENERATED_TT_ID_PREFIX}.
-     */
-    private static final String ERR_RESERVED_ID_PREFIX = "Reserved id prefix \""
-            +SCXML.GENERATED_TT_ID_PREFIX+"\" used for <{0} id=\"{1}\"> at {2}";
+        /**
+         * Whether to use the thread context {@link ClassLoader} for loading any {@link CustomAction} classes.
+         */
+        final boolean useContextClassLoaderForCustomActions;
 
-    /**
-     * Error message when the target of the URI fragment in a &lt;state&gt;'s
-     * &quot;src&quot; attribute is not defined in the referenced document.
-     */
-    private static final String ERR_UNSUPPORTED_TRANSITION_TYPE = "Unsupported transition type "
-            + "for <transition type=\"{0}\"> at {1}.";
+        // Mutable Commons SCXML object model configuration properties.
+        /**
+         * The parent SCXML document if this document is src'ed in via the &lt;state&gt; or &lt;parallel&gt; element's
+         * "src" attribute.
+         */
+        SCXML parent;
 
-    /**
-     * Error message when the target of the URI fragment in a &lt;state&gt;'s
-     * &quot;src&quot; attribute is not a &lt;state&gt; or &lt;final&gt; in
-     * the referenced document.
-     */
-    private static final String ERR_INVALID_VERSION = "The <scxml> element defines"
-            +" an unsupported version \"{0}\", only version \"1.0\" is supported.";
+        /**
+         * The Commons SCXML {@link PathResolver} to use for this document.
+         */
+        PathResolver pathResolver;
 
-    //------------------------- PUBLIC API METHODS -------------------------//
-    /*
-     * Public methods
-     */
-    /**
-     * Parse the SCXML document at the supplied path.
-     *
-     * @param scxmlPath The real path to the SCXML document.
-     *
-     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
-     *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     */
-    public static SCXML read(final String scxmlPath)
-            throws IOException, ModelException, XMLStreamException {
+        /**
+         * Whether to silently ignore any unknown or invalid elements
+         * or to leave warning logs for those.
+         */
+        boolean silent;
 
-        return read(scxmlPath, new Configuration());
-    }
+        /**
+         * Whether to strictly throw a model exception when there are any unknown or invalid elements
+         * or to leniently allow to read the model even with those.
+         */
+        boolean strict;
 
-    /**
-     * Parse the SCXML document at the supplied path with the given {@link Configuration}.
-     *
-     * @param scxmlPath The real path to the SCXML document.
-     * @param configuration The {@link Configuration} to use when parsing the SCXML document.
-     *
-     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
-     *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     */
-    public static SCXML read(final String scxmlPath, final Configuration configuration)
-            throws IOException, ModelException, XMLStreamException {
+        final ContentParser contentParser;
 
-        if (scxmlPath == null) {
-            throw new IllegalArgumentException(ERR_NULL_PATH);
+        /*
+         * Public constructors
+         */
+        /**
+         * Default constructor.
+         */
+        public Configuration() {
+            this(null, null);
         }
-        final SCXML scxml = readInternal(configuration, null, scxmlPath, null, null, null);
-        if (scxml != null) {
-            ModelUpdater.updateSCXML(scxml);
+
+        /**
+         * Package access copy constructor.
+         *
+         * @param source The source {@link Configuration} to replicate.
+         */
+        Configuration(final Configuration source) {
+            this(source.factoryId, source.factoryClassLoader, source.allocator, source.properties, source.resolver,
+                    source.reporter, source.encoding, source.systemId, source.validate, source.pathResolver,
+                    source.parent, source.customActions, source.customActionClassLoader,
+                    source.useContextClassLoaderForCustomActions, source.silent, source.strict);
         }
-        return scxml;
-    }
 
-    /**
-     * Parse the SCXML document at the supplied {@link URL}.
-     *
-     * @param scxmlURL The SCXML document {@link URL} to parse.
-     *
-     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
-     *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     */
-    public static SCXML read(final URL scxmlURL)
-            throws IOException, ModelException, XMLStreamException {
+        /**
+         * All purpose constructor. Any of the parameters passed in can be <code>null</code> (booleans should default
+         * to <code>false</code>).
+         *
+         * @param factoryId The <code>factoryId</code> to use.
+         * @param classLoader The {@link ClassLoader} to use for the {@link XMLInputFactory} instance to create.
+         * @param allocator The {@link XMLEventAllocator} for the {@link XMLInputFactory}.
+         * @param properties The map of properties (keys are property name strings, values are object property values)
+         *                   for the {@link XMLInputFactory}.
+         * @param resolver The {@link XMLResolver} for the {@link XMLInputFactory}.
+         * @param reporter The {@link XMLReporter} for the {@link XMLInputFactory}.
+         * @param encoding The <code>encoding</code> to use for the {@link XMLStreamReader}
+         * @param systemId The <code>systemId</code> to use for the {@link XMLStreamReader}
+         * @param validate Whether to validate the input with the XML Schema for SCXML.
+         * @param pathResolver The Commons SCXML {@link PathResolver} to use for this document.
+         * @param customActions The list of Commons SCXML custom actions that will be available for this document.
+         * @param customActionClassLoader The {@link ClassLoader} to use for the {@link CustomAction} instances to
+         *                                create.
+         * @param useContextClassLoaderForCustomActions Whether to use the thread context {@link ClassLoader} for the
+         *                                             {@link CustomAction} instances to create.
+         */
+        public Configuration(final String factoryId, final ClassLoader classLoader, final XMLEventAllocator allocator,
+                             final Map<String, Object> properties, final XMLResolver resolver, final XMLReporter reporter,
+                             final String encoding, final String systemId, final boolean validate, final PathResolver pathResolver,
+                             final List<CustomAction> customActions, final ClassLoader customActionClassLoader,
+                             final boolean useContextClassLoaderForCustomActions) {
+            this(factoryId, classLoader, allocator, properties, resolver, reporter, encoding, systemId, validate,
+                    pathResolver, null, customActions, customActionClassLoader,
+                    useContextClassLoaderForCustomActions);
+        }
 
-        return read(scxmlURL, new Configuration());
-    }
+        /**
+         * All-purpose package access constructor.
+         *
+         * @param factoryId The <code>factoryId</code> to use.
+         * @param factoryClassLoader The {@link ClassLoader} to use for the {@link XMLInputFactory} instance to
+         *                           create.
+         * @param allocator The {@link XMLEventAllocator} for the {@link XMLInputFactory}.
+         * @param properties The map of properties (keys are property name strings, values are object property values)
+         *                   for the {@link XMLInputFactory}.
+         * @param resolver The {@link XMLResolver} for the {@link XMLInputFactory}.
+         * @param reporter The {@link XMLReporter} for the {@link XMLInputFactory}.
+         * @param encoding The <code>encoding</code> to use for the {@link XMLStreamReader}
+         * @param systemId The <code>systemId</code> to use for the {@link XMLStreamReader}
+         * @param validate Whether to validate the input with the XML Schema for SCXML.
+         * @param pathResolver The Commons SCXML {@link PathResolver} to use for this document.
+         * @param parent The parent SCXML document if this document is src'ed in via the &lt;state&gt; or
+         *               &lt;parallel&gt; element's "src" attribute.
+         * @param customActions The list of Commons SCXML custom actions that will be available for this document.
+         * @param customActionClassLoader The {@link ClassLoader} to use for the {@link CustomAction} instances to
+         *                                create.
+         * @param useContextClassLoaderForCustomActions Whether to use the thread context {@link ClassLoader} for the
+         *                                             {@link CustomAction} instances to create.
+         */
+        Configuration(final String factoryId, final ClassLoader factoryClassLoader, final XMLEventAllocator allocator,
+                      final Map<String, Object> properties, final XMLResolver resolver, final XMLReporter reporter,
+                      final String encoding, final String systemId, final boolean validate, final PathResolver pathResolver,
+                      final SCXML parent, final List<CustomAction> customActions, final ClassLoader customActionClassLoader,
+                      final boolean useContextClassLoaderForCustomActions) {
+            this(factoryId, factoryClassLoader, allocator, properties, resolver, reporter, encoding, systemId,
+                    validate, pathResolver, parent, customActions, customActionClassLoader,
+                    useContextClassLoaderForCustomActions, false, false);
+        }
 
-    /**
-     * Parse the SCXML document at the supplied {@link URL} with the given {@link Configuration}.
-     *
-     * @param scxmlURL The SCXML document {@link URL} to parse.
-     * @param configuration The {@link Configuration} to use when parsing the SCXML document.
-     *
-     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
-     *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     */
-    public static SCXML read(final URL scxmlURL, final Configuration configuration)
-            throws IOException, ModelException, XMLStreamException {
+        /**
+         * All-purpose package access constructor.
+         *
+         * @param factoryId The <code>factoryId</code> to use.
+         * @param factoryClassLoader The {@link ClassLoader} to use for the {@link XMLInputFactory} instance to
+         *                           create.
+         * @param allocator The {@link XMLEventAllocator} for the {@link XMLInputFactory}.
+         * @param properties The map of properties (keys are property name strings, values are object property values)
+         *                   for the {@link XMLInputFactory}.
+         * @param resolver The {@link XMLResolver} for the {@link XMLInputFactory}.
+         * @param reporter The {@link XMLReporter} for the {@link XMLInputFactory}.
+         * @param encoding The <code>encoding</code> to use for the {@link XMLStreamReader}
+         * @param systemId The <code>systemId</code> to use for the {@link XMLStreamReader}
+         * @param validate Whether to validate the input with the XML Schema for SCXML.
+         * @param pathResolver The Commons SCXML {@link PathResolver} to use for this document.
+         * @param parent The parent SCXML document if this document is src'ed in via the &lt;state&gt; or
+         *               &lt;parallel&gt; element's "src" attribute.
+         * @param customActions The list of Commons SCXML custom actions that will be available for this document.
+         * @param customActionClassLoader The {@link ClassLoader} to use for the {@link CustomAction} instances to
+         *                                create.
+         * @param useContextClassLoaderForCustomActions Whether to use the thread context {@link ClassLoader} for the
+         *                                             {@link CustomAction} instances to create.
+         * @param silent Whether to silently ignore any unknown or invalid elements or to leave warning logs for those.
+         * @param strict Whether to strictly throw a model exception when there are any unknown or invalid elements
+         *               or to leniently allow to read the model even with those.
+         */
+        Configuration(final String factoryId, final ClassLoader factoryClassLoader, final XMLEventAllocator allocator,
+                      final Map<String, Object> properties, final XMLResolver resolver, final XMLReporter reporter,
+                      final String encoding, final String systemId, final boolean validate, final PathResolver pathResolver,
+                      final SCXML parent, final List<CustomAction> customActions, final ClassLoader customActionClassLoader,
+                      final boolean useContextClassLoaderForCustomActions, final boolean silent, final boolean strict) {
+            this.factoryId = factoryId;
+            this.factoryClassLoader = factoryClassLoader;
+            this.allocator = allocator;
+            this.properties = (properties == null ? new HashMap<>() : properties);
+            this.resolver = resolver;
+            this.reporter = reporter;
+            this.encoding = encoding;
+            this.systemId = systemId;
+            this.validate = validate;
+            this.pathResolver = pathResolver;
+            this.parent = parent;
+            this.customActions = (customActions == null ? new ArrayList<>() : customActions);
+            this.customActionClassLoader = customActionClassLoader;
+            this.useContextClassLoaderForCustomActions = useContextClassLoaderForCustomActions;
+            this.silent = silent;
+            this.strict = strict;
+            this.contentParser = new ContentParser();
+        }
 
-        if (scxmlURL == null) {
-            throw new IllegalArgumentException(ERR_NULL_URL);
+        /**
+         * Minimal convenience constructor.
+         *
+         * @param reporter The {@link XMLReporter} to use for this reading.
+         * @param pathResolver The Commons SCXML {@link PathResolver} to use for this reading.
+         */
+        public Configuration(final XMLReporter reporter, final PathResolver pathResolver) {
+            this(null, null, null, null, null, reporter, null, null, false, pathResolver, null, null, null, false);
         }
-        final SCXML scxml = readInternal(configuration, scxmlURL, null, null, null, null);
-        if (scxml != null) {
-            ModelUpdater.updateSCXML(scxml);
+
+        /**
+         * Convenience constructor.
+         *
+         * @param reporter The {@link XMLReporter} to use for this reading.
+         * @param pathResolver The Commons SCXML {@link PathResolver} to use for this reading.
+         * @param customActions The list of Commons SCXML custom actions that will be available for this document.
+         */
+        public Configuration(final XMLReporter reporter, final PathResolver pathResolver,
+                             final List<CustomAction> customActions) {
+            this(null, null, null, null, null, reporter, null, null, false, pathResolver, null, customActions, null,
+                    false);
         }
-        return scxml;
-    }
 
-    /**
-     * Parse the SCXML document supplied by the given {@link InputStream}.
-     *
-     * @param scxmlStream The {@link InputStream} supplying the SCXML document to parse.
-     *
-     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
-     *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     */
-    public static SCXML read(final InputStream scxmlStream)
-            throws IOException, ModelException, XMLStreamException {
+        /*
+         * Package access constructors
+         */
+        /**
+         * Convenience package access constructor.
+         *
+         * @param reporter The {@link XMLReporter} for the {@link XMLInputFactory}.
+         * @param pathResolver The Commons SCXML {@link PathResolver} to use for this document.
+         * @param parent The parent SCXML document if this document is src'ed in via the &lt;state&gt; or
+         *               &lt;parallel&gt; element's "src" attribute.
+         */
+        Configuration(final XMLReporter reporter, final PathResolver pathResolver, final SCXML parent) {
+            this(null, null, null, null, null, reporter, null, null, false, pathResolver, parent, null, null, false);
+        }
 
-        return read(scxmlStream, new Configuration());
-    }
+        /*
+         * Package access convenience methods
+         */
 
-    /**
-     * Parse the SCXML document supplied by the given {@link InputStream} with the given {@link Configuration}.
-     *
-     * @param scxmlStream The {@link InputStream} supplying the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use when parsing the SCXML document.
-     *
-     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
-     *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     */
-    public static SCXML read(final InputStream scxmlStream, final Configuration configuration)
-            throws IOException, ModelException, XMLStreamException {
+        /**
+         * Returns true if it is set to read models silently without any model error warning logs.
+         * @return true if it is set to read models silently without any model error warning logs
+         * @see #silent
+         */
+        public boolean isSilent() {
+            return silent;
+        }
 
-        if (scxmlStream == null) {
-            throw new IllegalArgumentException(ERR_NULL_ISTR);
+        /**
+         * Returns true if it is set to check model strictly with throwing exceptions on any model error.
+         * @return true if it is set to check model strictly with throwing exceptions on any model error
+         * @see #strict
+         */
+        public boolean isStrict() {
+            return strict;
         }
-        final SCXML scxml = readInternal(configuration, null, null, scxmlStream, null, null);
-        if (scxml != null) {
-            ModelUpdater.updateSCXML(scxml);
+
+        /**
+         * Turn on/off silent mode (whether to read models silently without any model error warning logs)
+         * @param silent silent mode (whether to read models silently without any model error warning logs)
+         * @see #silent
+         */
+        public void setSilent(final boolean silent) {
+            this.silent = silent;
+        }
+
+        /**
+         * Turn on/off strict model (whether to check model strictly with throwing exception on any model error)
+         * @param strict strict model (whether to check model strictly with throwing exception on any model error)
+         * @see #strict
+         */
+        public void setStrict(final boolean strict) {
+            this.strict = strict;
         }
-        return scxml;
     }
 
+    private static final org.apache.commons.logging.Log logger = LogFactory.getLog(SCXMLReader.class);
+
     /**
-     * Parse the SCXML document supplied by the given {@link Reader}.
-     *
-     * @param scxmlReader The {@link Reader} supplying the SCXML document to parse.
-     *
-     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
-     *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * By default Sun/Oracle XMLStreamReader implementation doesn't report CDATA events.
+     * This can be turned on (as needed by SCXMLReader) by setting this property TRUE
      */
-    public static SCXML read(final Reader scxmlReader)
-            throws IOException, ModelException, XMLStreamException {
+    public final static String XMLInputFactory_JDK_PROP_REPORT_CDATA = "http://java.sun.com/xml/stream/properties/report-cdata-event";
+    //---------------------- PRIVATE CONSTANTS ----------------------//
+    /**
+     * The version attribute value the SCXML element <em>must</em> have as stated by the spec: 3.2.1
+     */
+    private static final String SCXML_REQUIRED_VERSION = "1.0";
 
-        return read(scxmlReader, new Configuration());
-    }
+    /**
+     * The default namespace for attributes.
+     */
+    private static final String XMLNS_DEFAULT = null;
 
+    //---- ERROR MESSAGES ----//
     /**
-     * Parse the SCXML document supplied by the given {@link Reader} with the given {@link Configuration}.
-     *
-     * @param scxmlReader The {@link Reader} supplying the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use when parsing the SCXML document.
-     *
-     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
-     *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * Null URL passed as argument.
      */
-    public static SCXML read(final Reader scxmlReader, final Configuration configuration)
-            throws IOException, ModelException, XMLStreamException {
+    private static final String ERR_NULL_URL = "Cannot parse null URL";
 
-        if (scxmlReader == null) {
-            throw new IllegalArgumentException(ERR_NULL_READ);
-        }
-        final SCXML scxml = readInternal(configuration, null, null, null, scxmlReader, null);
-        if (scxml != null) {
-            ModelUpdater.updateSCXML(scxml);
-        }
-        return scxml;
-    }
+    /**
+     * Null path passed as argument.
+     */
+    private static final String ERR_NULL_PATH = "Cannot parse null path";
 
     /**
-     * Parse the SCXML document supplied by the given {@link Source}.
-     *
-     * @param scxmlSource The {@link Source} supplying the SCXML document to parse.
-     *
-     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
-     *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * Null InputStream passed as argument.
      */
-    public static SCXML read(final Source scxmlSource)
-            throws IOException, ModelException, XMLStreamException {
+    private static final String ERR_NULL_ISTR = "Cannot parse null InputStream";
 
-        return read(scxmlSource, new Configuration());
-    }
+    /**
+     * Null Reader passed as argument.
+     */
+    private static final String ERR_NULL_READ = "Cannot parse null Reader";
 
     /**
-     * Parse the SCXML document supplied by the given {@link Source} with the given {@link Configuration}.
-     *
-     * @param scxmlSource The {@link Source} supplying the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use when parsing the SCXML document.
-     *
-     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
-     *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * Null Source passed as argument.
      */
-    public static SCXML read(final Source scxmlSource, final Configuration configuration)
-            throws IOException, ModelException, XMLStreamException {
+    private static final String ERR_NULL_SRC = "Cannot parse null Source";
 
-        if (scxmlSource == null) {
-            throw new IllegalArgumentException(ERR_NULL_SRC);
-        }
-        final SCXML scxml = readInternal(configuration, null, null, null, null, scxmlSource);
-        if (scxml != null) {
-            ModelUpdater.updateSCXML(scxml);
-        }
-        return scxml;
-    }
+    /**
+     * 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 actions list"
+            + " contained unknown object, class not a Commons SCXML Action class subtype: ";
 
-    //---------------------- PRIVATE UTILITY METHODS ----------------------//
     /**
-     * Parse the SCXML document at the supplied {@link URL} using the supplied {@link Configuration}, but do not
-     * wire up the object model to be usable just yet. Exactly one of the url, path, stream, reader or source
-     * parameters must be provided.
-     *
-     * @param configuration The {@link Configuration} to use when parsing the SCXML document.
-     * @param scxmlURL The optional SCXML document {@link URL} to parse.
-     * @param scxmlPath The optional real path to the SCXML document as a string.
-     * @param scxmlStream The optional {@link InputStream} providing the SCXML document.
-     * @param scxmlReader The optional {@link Reader} providing the SCXML document.
-     * @param scxmlSource The optional {@link Source} providing the SCXML document.
-     *
-     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document
-     *         (not wired up to be immediately usable).
-     *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * Parser configuration error while trying to parse stream to DOM node(s).
      */
-    private static SCXML readInternal(final Configuration configuration, final URL scxmlURL, final String scxmlPath,
-                                      final InputStream scxmlStream, final Reader scxmlReader, final Source scxmlSource)
-            throws IOException, ModelException, XMLStreamException {
+    private static final String ERR_PARSER_CFG = "ParserConfigurationException while trying"
+            + " to parse stream into DOM node(s).";
 
-        if (configuration.pathResolver == null) {
-            if (scxmlURL != null) {
-                configuration.pathResolver = new URLResolver(scxmlURL);
-            } else if (scxmlPath != null) {
-                configuration.pathResolver = new URLResolver(new URL(scxmlPath));
-            }
-        }
+    /**
+     * Error message when the URI in a &lt;state&gt;'s &quot;src&quot;
+     * attribute does not point to a valid SCXML document, and thus cannot be
+     * parsed.
+     */
+    private static final String ERR_STATE_SRC =
+            "Source attribute in <state src=\"{0}\"> cannot be parsed";
 
-        final XMLStreamReader reader = getReader(configuration, scxmlURL, scxmlPath, scxmlStream, scxmlReader, scxmlSource);
+    /**
+     * Error message when the target of the URI fragment in a &lt;state&gt;'s
+     * &quot;src&quot; attribute is not defined in the referenced document.
+     */
+    private static final String ERR_STATE_SRC_FRAGMENT = "URI Fragment in "
+            + "<state src=\"{0}\"> is an unknown state in referenced document";
 
-        return readDocument(reader, configuration);
-    }
+    /**
+     * Error message when the target of the URI fragment in a &lt;state&gt;'s
+     * &quot;src&quot; attribute is not a &lt;state&gt; or &lt;final&gt; in
+     * the referenced document.
+     */
+    private static final String ERR_STATE_SRC_FRAGMENT_TARGET = "URI Fragment"
+            + " in <state src=\"{0}\"> does not point to a <state> or <final>";
 
-    /*
-     * Private utility functions for reading the SCXML document.
+    /**
+     * Error message when the target of the URI fragment in a &lt;state&gt;'s
+     * &quot;src&quot; attribute is not a &lt;state&gt; or &lt;final&gt; in
+     * the referenced document.
      */
+    private static final String ERR_REQUIRED_ATTRIBUTE_MISSING = "<{0}> is missing"
+            +" required attribute \"{1}\" value at {2}";
+
     /**
-     * Read the SCXML document through the {@link XMLStreamReader}.
-     *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     *
-     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document
-     *         (not wired up to be immediately usable).
-     *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * Error message when the target of the URI fragment in a &lt;state&gt;'s
+     * &quot;src&quot; attribute is not a &lt;state&gt; or &lt;final&gt; in
+     * the referenced document.
      */
-    private static SCXML readDocument(final XMLStreamReader reader, final Configuration configuration)
-            throws IOException, ModelException, XMLStreamException {
+    private static final String ERR_ATTRIBUTE_NOT_BOOLEAN = "Illegal value \"{0}\""
+            + "for attribute \"{1}\" in element <{2}> at {3}."
+            +" Only the value \"true\" or \"false\" is allowed.";
 
-        final SCXML scxml = new SCXML();
-        scxml.setPathResolver(configuration.pathResolver);
-        while (reader.hasNext()) {
-            String name, nsURI;
-            switch (reader.next()) {
-                case XMLStreamConstants.START_ELEMENT:
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_SCXML.equals(name)) {
-                            readSCXML(reader, configuration, scxml);
-                        } else {
-                            reportIgnoredElement(reader, configuration, "DOCUMENT_ROOT", nsURI, name);
-                        }
-                    } else {
-                        reportIgnoredElement(reader, configuration, "DOCUMENT_ROOT", nsURI, name);
-                    }
-                    break;
-                case XMLStreamConstants.NAMESPACE:
-                    System.err.println(reader.getNamespaceCount());
-                    break;
-                default:
-            }
-        }
-        return scxml;
+    /**
+     * Error message when the element (state|parallel|final|history) uses an id value
+     * with the reserved prefix {@link SCXML#GENERATED_TT_ID_PREFIX}.
+     */
+    private static final String ERR_RESERVED_ID_PREFIX = "Reserved id prefix \""
+            +SCXML.GENERATED_TT_ID_PREFIX+"\" used for <{0} id=\"{1}\"> at {2}";
+
+    /**
+     * Error message when the target of the URI fragment in a &lt;state&gt;'s
+     * &quot;src&quot; attribute is not defined in the referenced document.
+     */
+    private static final String ERR_UNSUPPORTED_TRANSITION_TYPE = "Unsupported transition type "
+            + "for <transition type=\"{0}\"> at {1}.";
+
+    /**
+     * Error message when the target of the URI fragment in a &lt;state&gt;'s
+     * &quot;src&quot; attribute is not a &lt;state&gt; or &lt;final&gt; in
+     * the referenced document.
+     */
+    private static final String ERR_INVALID_VERSION = "The <scxml> element defines"
+            +" an unsupported version \"{0}\", only version \"1.0\" is supported.";
+
+    /**
+     * @param prefix prefix
+     * @param localName localName
+     * @return a qualified name from a prefix and localName
+     */
+    private static String createQualifiedName(final String prefix, final String localName) {
+        return (prefix != null && !prefix.isEmpty() ? prefix + ":" : "") + localName;
     }
 
     /**
-     * Read the contents of this &lt;scxml&gt; element.
+     * Use the supplied {@link Configuration} to create an appropriate {@link XMLStreamReader} for this
+     * {@link SCXMLReader}. Exactly one of the url, path, stream, reader or source parameters must be provided.
      *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param scxml The root of the object model being parsed.
+     * @param configuration The {@link Configuration} to be used.
+     * @param url The {@link URL} to the SCXML document to read.
+     * @param path The optional real path to the SCXML document as a string.
+     * @param stream The optional {@link InputStream} providing the SCXML document.
+     * @param reader The optional {@link Reader} providing the SCXML document.
+     * @param source The optional {@link Source} providing the SCXML document.
      *
-     * @throws IOException An IO error during parsing.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @return The appropriately configured {@link XMLStreamReader}.
+     *
+     * @throws IOException Exception with the URL IO.
+     * @throws XMLStreamException A problem with the XML stream creation or an wrapped {@link SAXException}
+     *                            thrown in trying to validate the document against the XML Schema for SCXML.
      */
-    private static void readSCXML(final XMLStreamReader reader, final Configuration configuration, final SCXML scxml)
-            throws IOException, ModelException, XMLStreamException {
+    private static XMLStreamReader getReader(final Configuration configuration, final URL url, final String path,
+                                             final InputStream stream, final Reader reader, final Source source)
+            throws IOException, XMLStreamException {
 
-        scxml.setDatamodelName(readAV(reader, SCXMLConstants.ATTR_DATAMODEL));
-        scxml.setExmode(readAV(reader, SCXMLConstants.ATTR_EXMODE));
-        scxml.setInitial(readAV(reader, SCXMLConstants.ATTR_INITIAL));
-        scxml.setName(readAV(reader, SCXMLConstants.ATTR_NAME));
-        scxml.setProfile(readAV(reader, SCXMLConstants.ATTR_PROFILE));
-        scxml.setVersion(readRequiredAV(reader, SCXMLConstants.ELEM_SCXML, SCXMLConstants.ATTR_VERSION));
-        final String binding = readAV(reader, SCXMLConstants.ATTR_BINDING);
-        if (binding != null) {
-            if (SCXMLConstants.ATTR_BINDING_LATE.equals(binding)) {
-                scxml.setLateBinding(true);
-            } else if (SCXMLConstants.ATTR_BINDING_EARLY.equals(binding)) {
-                scxml.setLateBinding(false);
-            } else {
-                reportIgnoredAttribute(reader, configuration, SCXMLConstants.ELEM_SCXML, SCXMLConstants.ATTR_BINDING, binding);
+        // Instantiate the XMLInputFactory
+        XMLInputFactory factory = XMLInputFactory.newInstance();
+        if (configuration.factoryId != null && configuration.factoryClassLoader != null) {
+            factory = XMLInputFactory.newFactory(configuration.factoryId, configuration.factoryClassLoader);
+        }
+        factory.setEventAllocator(configuration.allocator);
+        if (factory.isPropertySupported(XMLInputFactory_JDK_PROP_REPORT_CDATA)) {
+            factory.setProperty(XMLInputFactory_JDK_PROP_REPORT_CDATA, Boolean.TRUE);
+        }
+        for (final Map.Entry<String, Object> property : configuration.properties.entrySet()) {
+            if (factory.isPropertySupported(property.getKey())) {
+                factory.setProperty(property.getKey(), property.getValue());
             }
         }
-        if (!SCXML_REQUIRED_VERSION.equals(scxml.getVersion())) {
-            throw new ModelException(new MessageFormat(ERR_INVALID_VERSION).format(new Object[] {scxml.getVersion()}));
+        factory.setXMLReporter(configuration.reporter);
+        factory.setXMLResolver(configuration.resolver);
+
+        // Consolidate InputStream options
+        InputStream urlStream = null;
+        if (url != null || path != null) {
+            final URL scxml = (url != null ? url : new URL(path));
+            final URLConnection conn = scxml.openConnection();
+            conn.setUseCaches(false);
+            urlStream = conn.getInputStream();
+        } else if (stream != null) {
+            urlStream = stream;
         }
-        scxml.setNamespaces(readNamespaces(reader));
 
-        boolean hasGlobalScript = false;
+        // Create the XMLStreamReader
+        XMLStreamReader xsr = null;
 
-        loop : while (reader.hasNext()) {
-            String name, nsURI;
-            switch (reader.next()) {
-                case XMLStreamConstants.START_ELEMENT:
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_STATE.equals(name)) {
-                            readState(reader, configuration, scxml, null);
-                        } else if (SCXMLConstants.ELEM_PARALLEL.equals(name)) {
-                            readParallel(reader, configuration, scxml, null);
-                        } else if (SCXMLConstants.ELEM_FINAL.equals(name)) {
-                            readFinal(reader, configuration, scxml, null);
-                        } else if (SCXMLConstants.ELEM_DATAMODEL.equals(name)) {
-                            readDatamodel(reader, configuration, scxml, null);
-                        } else if (SCXMLConstants.ELEM_SCRIPT.equals(name) && !hasGlobalScript) {
-                            readGlobalScript(reader, configuration, scxml);
-                            hasGlobalScript = true;
-                        } else {
-                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_SCXML, nsURI, name);
-                        }
-                    } else {
-                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_SCXML, nsURI, name);
-                    }
-                    break;
-                case XMLStreamConstants.END_ELEMENT:
-                    break loop;
-                default:
+        if (configuration.validate) {
+            // Validation requires us to use a Source
+
+            final URL scxmlSchema = new URL("TODO"); // TODO, point to appropriate location
+            final SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
+            Schema schema;
+            try {
+                schema = schemaFactory.newSchema(scxmlSchema);
+            } catch (final SAXException se) {
+                throw new XMLStreamException("Failed to create SCXML Schema for validation", se);
+            }
+
+            final Validator validator = schema.newValidator();
+            validator.setErrorHandler(new SimpleErrorHandler());
+
+            Source src = null;
+            if (urlStream != null) {
+                // configuration.encoding is ignored
+                if (configuration.systemId != null) {
+                    src = new StreamSource(urlStream, configuration.systemId);
+                } else {
+                    src = new StreamSource(urlStream);
+                }
+            } else if (reader != null) {
+                if (configuration.systemId != null) {
+                    src = new StreamSource(reader, configuration.systemId);
+                } else {
+                    src = new StreamSource(reader);
+                }
+            } else if (source != null) {
+                src = source;
+            }
+            xsr = factory.createXMLStreamReader(src);
+            try {
+                validator.validate(src);
+            } catch (final SAXException se) {
+                throw new XMLStreamException("Failed to create apply SCXML Validator", se);
+            }
+
+        } else {
+            // We can use the more direct XMLInputFactory API if validation isn't needed
+
+            if (urlStream != null) {
+                // systemId gets preference, then encoding if either are present
+                if (configuration.systemId != null) {
+                    xsr = factory.createXMLStreamReader(configuration.systemId, urlStream);
+                } else if (configuration.encoding != null) {
+                    xsr = factory.createXMLStreamReader(urlStream, configuration.encoding);
+                } else {
+                    xsr = factory.createXMLStreamReader(urlStream);
+                }
+            } else if (reader != null) {
+                if (configuration.systemId != null) {
+                    xsr = factory.createXMLStreamReader(configuration.systemId, reader);
+                } else {
+                    xsr = factory.createXMLStreamReader(reader);
+                }
+            } else if (source != null) {
+                xsr = factory.createXMLStreamReader(source);
             }
+
         }
+
+        return xsr;
     }
 
     /**
-     * Read the contents of this &lt;state&gt; element.
+     * @param input input string to check if null or empty after trim
+     * @return null if input is null or empty after trim()
+     */
+    private static String nullIfEmpty(final String input) {
+        return input == null || input.trim().length()==0 ? null : input.trim();
+    }
+
+    /**
+     * Parse the SCXML document supplied by the given {@link InputStream}.
      *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param scxml The root of the object model being parsed.
-     * @param parent The parent {@link TransitionalState} for this state (null for top level state).
+     * @param scxmlStream The {@link InputStream} supplying the SCXML document to parse.
+     *
+     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
      *
      * @throws IOException An IO error during parsing.
      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
      *                        errors in the SCXML document that may not be identified by the schema).
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static void readState(final XMLStreamReader reader, final Configuration configuration, final SCXML scxml,
-                                  final TransitionalState parent)
+    public static SCXML read(final InputStream scxmlStream)
             throws IOException, ModelException, XMLStreamException {
 
-        final State state = new State();
-        state.setId(readOrGeneratedTransitionTargetId(reader, scxml, SCXMLConstants.ELEM_STATE));
-        final String initial = readAV(reader, SCXMLConstants.ATTR_INITIAL);
-        if (initial != null) {
-            state.setFirst(initial);
-        }
-        final String src = readAV(reader, SCXMLConstants.ATTR_SRC);
-        if (src != null) {
-            String source = src;
-            final Configuration copy = new Configuration(configuration);
-            if (copy.parent == null) {
-                copy.parent = scxml;
-            }
-            if (configuration.pathResolver != null) {
-                source = configuration.pathResolver.resolvePath(src);
-                copy.pathResolver = configuration.pathResolver.getResolver(src);
-            }
-            readTransitionalStateSrc(copy, source, state);
-        }
-
-        if (parent == null) {
-            scxml.addChild(state);
-        } else if (parent instanceof State) {
-            ((State)parent).addChild(state);
-        }
-        else {
-            ((Parallel)parent).addChild(state);
-        }
-        scxml.addTarget(state);
-        if (configuration.parent != null) {
-            configuration.parent.addTarget(state);
-        }
-
-        loop : while (reader.hasNext()) {
-            String name, nsURI;
-            switch (reader.next()) {
-                case XMLStreamConstants.START_ELEMENT:
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_TRANSITION.equals(name)) {
-                            state.addTransition(readTransition(reader, configuration));
-                        } else if (SCXMLConstants.ELEM_STATE.equals(name)) {
-                            readState(reader, configuration, scxml, state);
-                        } else if (SCXMLConstants.ELEM_INITIAL.equals(name)) {
-                            readInitial(reader, configuration, state);
-                        } else if (SCXMLConstants.ELEM_FINAL.equals(name)) {
-                            readFinal(reader, configuration, scxml, state);
-                        } else if (SCXMLConstants.ELEM_ONENTRY.equals(name)) {
-                            readOnEntry(reader, configuration, state);
-                        } else if (SCXMLConstants.ELEM_ONEXIT.equals(name)) {
-                            readOnExit(reader, configuration, state);
-                        } else if (SCXMLConstants.ELEM_PARALLEL.equals(name)) {
-                            readParallel(reader, configuration, scxml, state);
-                        } else if (SCXMLConstants.ELEM_DATAMODEL.equals(name)) {
-                            readDatamodel(reader, configuration, null, state);
-                        } else if (SCXMLConstants.ELEM_INVOKE.equals(name)) {
-                            readInvoke(reader, configuration, state);
-                        } else if (SCXMLConstants.ELEM_HISTORY.equals(name)) {
-                            readHistory(reader, configuration, scxml, state);
-                        } else {
-                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_STATE, nsURI, name);
-                        }
-                    } else {
-                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_STATE, nsURI, name);
-                    }
-                    break;
-                case XMLStreamConstants.END_ELEMENT:
-                    break loop;
-                default:
-            }
-        }
+        return read(scxmlStream, new Configuration());
     }
 
     /**
-     * Read the contents of this &lt;parallel&gt; element.
+     * Parse the SCXML document supplied by the given {@link InputStream} with the given {@link Configuration}.
      *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param scxml The root of the object model being parsed.
-     * @param parent The parent {@link TransitionalState} for this parallel (null for top level state).
+     * @param scxmlStream The {@link InputStream} supplying the SCXML document to parse.
+     * @param configuration The {@link Configuration} to use when parsing the SCXML document.
+     *
+     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
      *
      * @throws IOException An IO error during parsing.
      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
      *                        errors in the SCXML document that may not be identified by the schema).
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static void readParallel(final XMLStreamReader reader, final Configuration configuration, final SCXML scxml,
-                                     final TransitionalState parent)
+    public static SCXML read(final InputStream scxmlStream, final Configuration configuration)
             throws IOException, ModelException, XMLStreamException {
 
-        final Parallel parallel = new Parallel();
-        parallel.setId(readOrGeneratedTransitionTargetId(reader, scxml, SCXMLConstants.ELEM_PARALLEL));
-        final String src = readAV(reader, SCXMLConstants.ATTR_SRC);
-        if (src != null) {
-            String source = src;
-            final Configuration copy = new Configuration(configuration);
-            if (copy.parent == null) {
-                copy.parent = scxml;
-            }
-            if (configuration.pathResolver != null) {
-                source = configuration.pathResolver.resolvePath(src);
-                copy.pathResolver = configuration.pathResolver.getResolver(src);
-            }
-            readTransitionalStateSrc(copy, source, parallel);
-        }
-
-        if (parent == null) {
-            scxml.addChild(parallel);
-        } else if (parent instanceof State) {
-            ((State)parent).addChild(parallel);
-        }
-        else {
-            ((Parallel)parent).addChild(parallel);
-        }
-        scxml.addTarget(parallel);
-        if (configuration.parent != null) {
-            configuration.parent.addTarget(parallel);
+        if (scxmlStream == null) {
+            throw new IllegalArgumentException(ERR_NULL_ISTR);
         }
-
-        loop : while (reader.hasNext()) {
-            String name, nsURI;
-            switch (reader.next()) {
-                case XMLStreamConstants.START_ELEMENT:
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_TRANSITION.equals(name)) {
-                            parallel.addTransition(readTransition(reader, configuration));
-                        } else if (SCXMLConstants.ELEM_STATE.equals(name)) {
-                            readState(reader, configuration, scxml, parallel);
-                        } else if (SCXMLConstants.ELEM_PARALLEL.equals(name)) {
-                            readParallel(reader, configuration, scxml, parallel);
-                        } else if (SCXMLConstants.ELEM_ONENTRY.equals(name)) {
-                            readOnEntry(reader, configuration, parallel);
-                        } else if (SCXMLConstants.ELEM_ONEXIT.equals(name)) {
-                            readOnExit(reader, configuration, parallel);
-                        } else if (SCXMLConstants.ELEM_DATAMODEL.equals(name)) {
-                            readDatamodel(reader, configuration, null, parallel);
-                        } else if (SCXMLConstants.ELEM_INVOKE.equals(name)) {
-                            readInvoke(reader, configuration, parallel);
-                        } else if (SCXMLConstants.ELEM_HISTORY.equals(name)) {
-                            readHistory(reader, configuration, scxml, parallel);
-                        } else {
-                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_PARALLEL, nsURI, name);
-                        }
-                    } else {
-                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_PARALLEL, nsURI, name);
-                    }
-                    break;
-                case XMLStreamConstants.END_ELEMENT:
-                    break loop;
-                default:
-            }
+        final SCXML scxml = readInternal(configuration, null, null, scxmlStream, null, null);
+        if (scxml != null) {
+            ModelUpdater.updateSCXML(scxml);
         }
+        return scxml;
     }
 
     /**
-     * Read the contents of this &lt;final&gt; element.
+     * Parse the SCXML document supplied by the given {@link Reader}.
      *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param scxml The root of the object model being parsed.
-     * @param parent The parent {@link State} for this final (null for top level state).
+     * @param scxmlReader The {@link Reader} supplying the SCXML document to parse.
+     *
+     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
      *
      * @throws IOException An IO error during parsing.
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
      *                        errors in the SCXML document that may not be identified by the schema).
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static void readFinal(final XMLStreamReader reader, final Configuration configuration, final SCXML scxml,
-                                  final State parent)
-            throws XMLStreamException, ModelException, IOException {
+    public static SCXML read(final Reader scxmlReader)
+            throws IOException, ModelException, XMLStreamException {
 
-        final Final end = new Final();
-        end.setId(readOrGeneratedTransitionTargetId(reader, scxml, SCXMLConstants.ELEM_FINAL));
+        return read(scxmlReader, new Configuration());
+    }
 
-        if (parent == null) {
-            scxml.addChild(end);
-        } else {
-            parent.addChild(end);
-        }
+    /**
+     * Parse the SCXML document supplied by the given {@link Reader} with the given {@link Configuration}.
+     *
+     * @param scxmlReader The {@link Reader} supplying the SCXML document to parse.
+     * @param configuration The {@link Configuration} to use when parsing the SCXML document.
+     *
+     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
+     *
+     * @throws IOException An IO error during parsing.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     */
+    public static SCXML read(final Reader scxmlReader, final Configuration configuration)
+            throws IOException, ModelException, XMLStreamException {
 
-        scxml.addTarget(end);
-        if (configuration.parent != null) {
-            configuration.parent.addTarget(end);
+        if (scxmlReader == null) {
+            throw new IllegalArgumentException(ERR_NULL_READ);
         }
-
-        loop : while (reader.hasNext()) {
-            String name, nsURI;
-            switch (reader.next()) {
-                case XMLStreamConstants.START_ELEMENT:
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_ONENTRY.equals(name)) {
-                            readOnEntry(reader, configuration, end);
-                        } else if (SCXMLConstants.ELEM_ONEXIT.equals(name)) {
-                            readOnExit(reader, configuration, end);
-                        } else if (SCXMLConstants.ELEM_DONEDATA.equals(name) && end.getDoneData() == null) {
-                            readDoneData(reader, configuration, end);
-                        } else {
-                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_FINAL, nsURI, name);
-                        }
-                    } else {
-                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_FINAL, nsURI, name);
-                    }
-                    break;
-                case XMLStreamConstants.END_ELEMENT:
-                    break loop;
-                default:
-            }
+        final SCXML scxml = readInternal(configuration, null, null, null, scxmlReader, null);
+        if (scxml != null) {
+            ModelUpdater.updateSCXML(scxml);
         }
+        return scxml;
     }
 
     /**
-     * Read the contents of this &lt;donedata&gt; element.
+     * Parse the SCXML document supplied by the given {@link Source}.
      *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param parent The parent {@link State} for this final (null for top level state).
+     * @param scxmlSource The {@link Source} supplying the SCXML document to parse.
      *
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
+     *
+     * @throws IOException An IO error during parsing.
      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
      *                        errors in the SCXML document that may not be identified by the schema).
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static void readDoneData(final XMLStreamReader reader, final Configuration configuration, final Final parent)
-            throws XMLStreamException, ModelException {
-
-        final DoneData doneData = new DoneData();
-        parent.setDoneData(doneData);
+    public static SCXML read(final Source scxmlSource)
+            throws IOException, ModelException, XMLStreamException {
 
-        loop : while (reader.hasNext()) {
-            String name, nsURI;
-            switch (reader.next()) {
-                case XMLStreamConstants.START_ELEMENT:
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_PARAM.equals(name)) {
-                            if (doneData.getContent() == null) {
-                                readParam(reader, configuration, doneData);
-                            }
-                            else {
-                                reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_DONEDATA, nsURI, name);
-                            }
-                        } else if (SCXMLConstants.ELEM_CONTENT.equals(name)) {
-                            if (doneData.getParams().isEmpty()) {
-                                readContent(reader, configuration, doneData);
-                            }
-                            else {
-                                reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_DONEDATA, nsURI, name);
-                            }
-                        } else {
-                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_DONEDATA, nsURI, name);
-                        }
-                    } else {
-                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_DONEDATA, nsURI, name);
-                    }
-                    break;
-                case XMLStreamConstants.END_ELEMENT:
-                    break loop;
-                default:
-            }
-        }
+        return read(scxmlSource, new Configuration());
     }
 
     /**
-     * Parse the contents of the SCXML document that this "src" attribute value of a &lt;state&gt; or &lt;parallel&gt;
-     * element points to. Without a URL fragment, the entire state machine is imported as contents of the
-     * &lt;state&gt; or &lt;parallel&gt;. If a URL fragment is present, the fragment must specify the id of the
-     * corresponding &lt;state&gt; or &lt;parallel&gt; to import.
+     * Parse the SCXML document supplied by the given {@link Source} with the given {@link Configuration}.
      *
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param src The "src" attribute value.
-     * @param ts The parent {@link TransitionalState} that specifies this "src" attribute.
+     * @param scxmlSource The {@link Source} supplying the SCXML document to parse.
+     * @param configuration The {@link Configuration} to use when parsing the SCXML document.
      *
+     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
+     *
+     * @throws IOException An IO error during parsing.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static void readTransitionalStateSrc(final Configuration configuration, final String src,
-                                                 final TransitionalState ts)
-            throws ModelException {
+    public static SCXML read(final Source scxmlSource, final Configuration configuration)
+            throws IOException, ModelException, XMLStreamException {
 
-        // Check for URI fragment
-        final String[] fragments = src.split("#", 2);
-        final String location = fragments[0];
-        String fragment = null;
-        if (fragments.length > 1) {
-            fragment = fragments[1];
+        if (scxmlSource == null) {
+            throw new IllegalArgumentException(ERR_NULL_SRC);
         }
-
-        // Parse external document
-        SCXML externalSCXML;
-        try {
-            externalSCXML = SCXMLReader.readInternal(configuration, new URL(location), null, null, null, null);
-        } catch (final Exception e) {
-            final MessageFormat msgFormat = new MessageFormat(ERR_STATE_SRC);
-            final String errMsg = msgFormat.format(new Object[] {src});
-            throw new ModelException(errMsg + " : " + e.getMessage(), e);
+        final SCXML scxml = readInternal(configuration, null, null, null, null, scxmlSource);
+        if (scxml != null) {
+            ModelUpdater.updateSCXML(scxml);
         }
+        return scxml;
+    }
 
-        // Pull in the parts of the external document as needed
-        if (fragment == null) {
-            // All targets pulled in since its not a src fragment
-            if (ts instanceof State) {
-                final State s = (State) ts;
-                final Initial ini = new Initial();
-                final SimpleTransition t = new SimpleTransition();
-                t.setNext(externalSCXML.getInitial());
-                ini.setTransition(t);
-                s.setInitial(ini);
-                for (final EnterableState child : externalSCXML.getChildren()) {
-                    s.addChild(child);
-                }
-                s.setDatamodel(externalSCXML.getDatamodel());
-            } else if (ts instanceof Parallel) {
-                // TODO src attribute for <parallel>
-            }
-        } else {
-            // Need to pull in only descendent targets
-            final Object source = externalSCXML.getTargets().get(fragment);
-            if (source == null) {
-                final MessageFormat msgFormat = new MessageFormat(ERR_STATE_SRC_FRAGMENT);
-                final String errMsg = msgFormat.format(new Object[] {src});
-                throw new ModelException(errMsg);
-            }
-            if (source instanceof State && ts instanceof State) {
-                final State s = (State) ts;
-                final State include = (State) source;
-                for (final OnEntry onentry : include.getOnEntries()) {
-                    s.addOnEntry(onentry);
-                }
-                for (final OnExit onexit : include.getOnExits()) {
-                    s.addOnExit(onexit);
-                }
-                s.setDatamodel(include.getDatamodel());
-                final List<History> histories = include.getHistory();
-                for (final History h : histories) {
-                    s.addHistory(h);
-                    configuration.parent.addTarget(h);
-                }
-                for (final EnterableState child : include.getChildren()) {
-                    s.addChild(child);
-                    configuration.parent.addTarget(child);
-                    readInExternalTargets(configuration.parent, child);
-                }
-                for (final Invoke invoke : include.getInvokes()) {
-                    s.addInvoke(invoke);
-                }
-                if (include.getInitial() != null) {
-                    s.setInitial(include.getInitial());
-                }
-                final List<Transition> transitions = include.getTransitionsList();
-                for (final Transition t : transitions) {
-                    s.addTransition(t);
-                }
-            } else if (ts instanceof Parallel && source instanceof Parallel) {
-                // TODO src attribute for <parallel>
-            } else {
-                final MessageFormat msgFormat =
-                        new MessageFormat(ERR_STATE_SRC_FRAGMENT_TARGET);
-                final String errMsg = msgFormat.format(new Object[] {src});
-                throw new ModelException(errMsg);
-            }
-        }
-    }
-
-    /**
-     * Add all the nested targets from given target to given parent state machine.
-     *
-     * @param parent The state machine
-     * @param es The target to import
+    //------------------------- PUBLIC API METHODS -------------------------//
+    /*
+     * Public methods
      */
-    private static void readInExternalTargets(final SCXML parent, final EnterableState es) {
-        if (es instanceof TransitionalState) {
-            for (final History h : ((TransitionalState)es).getHistory()) {
-                parent.addTarget(h);
-            }
-            for (final EnterableState child : ((TransitionalState) es).getChildren()) {
-                parent.addTarget(child);
-                readInExternalTargets(parent, child);
-            }
-        }
-    }
-
     /**
-     * Read the contents of this &lt;datamodel&gt; element.
+     * Parse the SCXML document at the supplied path.
      *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param scxml The root of the object model being parsed.
-     * @param parent The parent {@link TransitionalState} for this datamodel (null for top level).
+     * @param scxmlPath The real path to the SCXML document.
      *
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
+     *
+     * @throws IOException An IO error during parsing.
      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
      *                        errors in the SCXML document that may not be identified by the schema).
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static void readDatamodel(final XMLStreamReader reader, final Configuration configuration,
-                                      final SCXML scxml, final TransitionalState parent)
-            throws XMLStreamException, ModelException {
-
-        final Datamodel dm = new Datamodel();
-
-        loop : while (reader.hasNext()) {
-            String name, nsURI;
-            switch (reader.next()) {
-                case XMLStreamConstants.START_ELEMENT:
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_DATA.equals(name)) {
-                            readData(reader, configuration, dm);
-                        } else {
-                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_DATAMODEL, nsURI, name);
-                        }
-                    } else {
-                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_DATAMODEL, nsURI, name);
-                    }
-                    break;
-                case XMLStreamConstants.END_ELEMENT:
-                    break loop;
-                default:
-            }
-        }
+    public static SCXML read(final String scxmlPath)
+            throws IOException, ModelException, XMLStreamException {
 
-        if (parent == null) {
-            scxml.setDatamodel(dm);
-        } else {
-            parent.setDatamodel(dm);
-        }
+        return read(scxmlPath, new Configuration());
     }
 
     /**
-     * Read the contents of this &lt;data&gt; element.
+     * Parse the SCXML document at the supplied path with the given {@link Configuration}.
      *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param dm The parent {@link Datamodel} for this data.
+     * @param scxmlPath The real path to the SCXML document.
+     * @param configuration The {@link Configuration} to use when parsing the SCXML document.
      *
+     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
+     *
+     * @throws IOException An IO error during parsing.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static void readData(final XMLStreamReader reader, final Configuration configuration, final Datamodel dm)
-            throws XMLStreamException, ModelException {
-
-        final Data datum = new Data();
-        datum.setId(readRequiredAV(reader, SCXMLConstants.ELEM_DATA, SCXMLConstants.ATTR_ID));
-        final String expr = readAV(reader, SCXMLConstants.ATTR_EXPR);
-        final String src = readAV(reader, SCXMLConstants.ATTR_SRC);
+    public static SCXML read(final String scxmlPath, final Configuration configuration)
+            throws IOException, ModelException, XMLStreamException {
 
-        if (expr != null) {
-            if (src != null) {
-                reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_DATA, SCXMLConstants.ATTR_EXPR, SCXMLConstants.ATTR_SRC);
-            }
-            datum.setExpr(expr);
-            skipToEndElement(reader);
-        } else if (src != null ) {
-            datum.setSrc(src);
-            skipToEndElement(reader);
-        } else {
-            readParsedValue(reader, configuration, datum, false);
+        if (scxmlPath == null) {
+            throw new IllegalArgumentException(ERR_NULL_PATH);
         }
-        dm.addData(datum);
+        final SCXML scxml = readInternal(configuration, null, scxmlPath, null, null, null);
+        if (scxml != null) {
+            ModelUpdater.updateSCXML(scxml);
+        }
+        return scxml;
     }
 
     /**
-     * Read the contents of this &lt;invoke&gt; element.
+     * Parse the SCXML document at the supplied {@link URL}.
      *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param parent The parent {@link TransitionalState} for this invoke.
+     * @param scxmlURL The SCXML document {@link URL} to parse.
      *
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
+     *
+     * @throws IOException An IO error during parsing.
      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
      *                        errors in the SCXML document that may not be identified by the schema).
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static void readInvoke(final XMLStreamReader reader, final Configuration configuration,
-                                   final TransitionalState parent)
-            throws XMLStreamException, ModelException {
-
-        final Invoke invoke = new Invoke();
-        invoke.setId(readAV(reader, SCXMLConstants.ATTR_ID));
-        invoke.setIdlocation(readAV(reader, SCXMLConstants.ATTR_IDLOCATION));
-        invoke.setSrc(readAV(reader, SCXMLConstants.ATTR_SRC));
-        invoke.setSrcexpr(readAV(reader, SCXMLConstants.ATTR_SRCEXPR));
-        invoke.setType(readAV(reader, SCXMLConstants.ATTR_TYPE));
-        invoke.setAutoForward(readBooleanAV(reader, SCXMLConstants.ELEM_INVOKE, SCXMLConstants.ATTR_AUTOFORWARD));
-        invoke.setNamelist(readAV(reader, SCXMLConstants.ATTR_NAMELIST));
-
-        loop : while (reader.hasNext()) {
-            String name, nsURI;
-            switch (reader.next()) {
-                case XMLStreamConstants.START_ELEMENT:
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_PARAM.equals(name)) {
-                            readParam(reader, configuration, invoke);
-                        } else if (SCXMLConstants.ELEM_FINALIZE.equals(name)) {
-                            readFinalize(reader, configuration, parent, invoke);
-                        } else if (SCXMLConstants.ELEM_CONTENT.equals(name)) {
-                            readContent(reader, configuration, invoke);
-                        } else {
-                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_INVOKE, nsURI, name);
-                        }
-                    } else {
-                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_INVOKE, nsURI, name);
-                    }
-                    break;
-                case XMLStreamConstants.END_ELEMENT:
-                    break loop;
-                default:
-            }
-        }
+    public static SCXML read(final URL scxmlURL)
+            throws IOException, ModelException, XMLStreamException {
 
-        parent.addInvoke(invoke);
+        return read(scxmlURL, new Configuration());
     }
 
     /**
-     * Read the contents of this &lt;param&gt; element.
+     * Parse the SCXML document at the supplied {@link URL} with the given {@link Configuration}.
      *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param parent The parent {@link org.apache.commons.scxml2.model.ParamsContainer} for this param.
+     * @param scxmlURL The SCXML document {@link URL} to parse.
+     * @param configuration The {@link Configuration} to use when parsing the SCXML document.
+     *
+     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document.
      *
+     * @throws IOException An IO error during parsing.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static void readParam(final XMLStreamReader reader, final Configuration configuration,
-                                  final ParamsContainer parent)
-            throws XMLStreamException, ModelException {
+    public static SCXML read(final URL scxmlURL, final Configuration configuration)
+            throws IOException, ModelException, XMLStreamException {
 
-        final Param param = new Param();
-        param.setName(readRequiredAV(reader, SCXMLConstants.ELEM_PARAM, SCXMLConstants.ATTR_NAME));
-        final String location = readAV(reader, SCXMLConstants.ATTR_LOCATION);
-        final String expr = readAV(reader, SCXMLConstants.ATTR_EXPR);
-        if (expr != null) {
-            if (location != null) {
-                reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_PARAM, SCXMLConstants.ATTR_LOCATION, SCXMLConstants.ATTR_EXPR);
-            }
-            else {
-                param.setExpr(expr);
-            }
-        }
-        else if (location == null) {
-            // force error missing required location or expr: use location attr for this
-            param.setLocation(readRequiredAV(reader, SCXMLConstants.ELEM_PARAM, SCXMLConstants.ATTR_LOCATION));
+        if (scxmlURL == null) {
+            throw new IllegalArgumentException(ERR_NULL_URL);
         }
-        else {
-            param.setLocation(location);
+        final SCXML scxml = readInternal(configuration, scxmlURL, null, null, null, null);
+        if (scxml != null) {
+            ModelUpdater.updateSCXML(scxml);
         }
-        parent.getParams().add(param);
-        skipToEndElement(reader);
+        return scxml;
     }
 
     /**
-     * Read the contents of this &lt;finalize&gt; element.
+     * Read the contents of this &lt;assign&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
-     * @param state The {@link TransitionalState} which contains the parent {@link Invoke}.
-     * @param invoke The parent {@link Invoke} for this finalize.
+     * @param executable The parent {@link Executable} for this action.
+     * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static void readFinalize(final XMLStreamReader reader, final Configuration configuration,
-                                     final TransitionalState state, final Invoke invoke)
+    private static void readAssign(final XMLStreamReader reader, final Configuration configuration,
+                                   final Executable executable, final ActionsContainer parent)
             throws XMLStreamException, ModelException {
 
-        final Finalize finalize = new Finalize();
-        readExecutableContext(reader, configuration, finalize, null);
-        invoke.setFinalize(finalize);
-        finalize.setParent(state);
-    }
-
-    /**
-     * Read the contents of this &lt;content&gt; element.
-     *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param contentContainer The {@link ContentContainer} for this content.
-     *
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     */
-    private static void readContent(final XMLStreamReader reader, final Configuration configuration,
-                                    final ContentContainer contentContainer)
-            throws XMLStreamException, ModelException {
-
-        final Content content = new Content();
-        content.setExpr(readAV(reader, SCXMLConstants.ATTR_EXPR));
-        if (content.getExpr() != null) {
+        final Assign assign = new Assign();
+        assign.setExpr(readAV(reader, SCXMLConstants.ATTR_EXPR));
+        assign.setLocation(readRequiredAV(reader, SCXMLConstants.ELEM_ASSIGN, SCXMLConstants.ATTR_LOCATION));
+        assign.setSrc(readAV(reader, SCXMLConstants.ATTR_SRC));
+        if (assign.getExpr() != null && assign.getSrc() != null) {
+            reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_ASSIGN, SCXMLConstants.ATTR_EXPR, SCXMLConstants.ATTR_SRC);
+        }
+        else if (assign.getExpr() != null) {
+            skipToEndElement(reader);
+        }
+        else if (assign.getSrc() != null) {
             skipToEndElement(reader);
+            String resolvedSrc = assign.getSrc();
+            if (configuration.pathResolver != null) {
+                resolvedSrc = configuration.pathResolver.resolvePath(resolvedSrc);
+            }
+            try {
+                assign.setParsedValue(ContentParser.DEFAULT_PARSER.parseResource(resolvedSrc));
+            } catch (final IOException e) {
+                throw new ModelException(e);
+            }
         }
         else {
-            readParsedValue(reader, configuration, content, contentContainer instanceof Invoke);
+            final Location location = reader.getLocation();
+            readParsedValue(reader, configuration, assign, false);
+            if (assign.getParsedValue() == null) {
+                // report missing expression (as most common use-case)
+                reportMissingAttribute(location, SCXMLConstants.ELEM_ASSIGN, SCXMLConstants.ATTR_EXPR);
+            }
+        }
+
+        assign.setParent(executable);
+        if (parent != null) {
+            parent.addAction(assign);
+        } else {
+            executable.addAction(assign);
         }
-        contentContainer.setContent(content);
     }
 
     /**
-     * Read the contents of this &lt;initial&gt; element.
+     * Gets the attribute value at the current reader location.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param state The parent composite {@link State} for this initial.
+     * @param attrLocalName The attribute name whose value is needed.
      *
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
+     * @return The value of the attribute.
      */
-    private static void readInitial(final XMLStreamReader reader, final Configuration configuration,
-                                    final State state)
-            throws XMLStreamException, ModelException {
-
-        final Initial initial = new Initial();
-
-        loop : while (reader.hasNext()) {
-            String name, nsURI;
-            switch (reader.next()) {
-                case XMLStreamConstants.START_ELEMENT:
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_TRANSITION.equals(name)) {
-                            initial.setTransition(readSimpleTransition(reader, configuration));
-                        } else {
-                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_INITIAL, nsURI, name);
-                        }
-                    } else {
-                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_INITIAL, nsURI, name);
-                    }
-                    break;
-                case XMLStreamConstants.END_ELEMENT:
-                    break loop;
-                default:
-            }
-        }
-
-        state.setInitial(initial);
+    private static String readAV(final XMLStreamReader reader, final String attrLocalName) {
+        return nullIfEmpty(reader.getAttributeValue(XMLNS_DEFAULT, attrLocalName));
     }
 
     /**
-     * Read the contents of this &lt;history&gt; element.
+     * Read the following body contents into a String.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param scxml The root of the object model being parsed.
-     * @param ts The parent {@link org.apache.commons.scxml2.model.TransitionalState} for this history.
+     *
+     * @return The body content read into a String.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static void readHistory(final XMLStreamReader reader, final Configuration configuration,
-                                    final SCXML scxml, final TransitionalState ts)
-            throws XMLStreamException, ModelException {
-
-        final History history = new History();
-        history.setId(readOrGeneratedTransitionTargetId(reader, scxml, SCXMLConstants.ELEM_HISTORY));
-        history.setType(readAV(reader, SCXMLConstants.ATTR_TYPE));
+    private static String readBody(final XMLStreamReader reader)
+            throws XMLStreamException {
 
-        ts.addHistory(history);
-        scxml.addTarget(history);
+        final StringBuilder body = new StringBuilder();
 
+        // Add all body content to StringBuilder
         loop : while (reader.hasNext()) {
-            String name, nsURI;
             switch (reader.next()) {
                 case XMLStreamConstants.START_ELEMENT:
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_TRANSITION.equals(name)) {
-                            history.setTransition(readTransition(reader, configuration));
-                        } else {
-                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_HISTORY, nsURI, name);
-                        }
-                    } else {
-                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_HISTORY, nsURI, name);
-                    }
+                    logger.warn("Ignoring XML content in <script> element, encountered element: "
+                            + createQualifiedName(reader.getPrefix(), reader.getLocalName()));
+                    skipToEndElement(reader);
+                    break;
+                case XMLStreamConstants.SPACE:
+                case XMLStreamConstants.CHARACTERS:
+                case XMLStreamConstants.ENTITY_REFERENCE:
+                case XMLStreamConstants.CDATA:
+                    body.append(reader.getText());
+                    break;
+                case XMLStreamConstants.COMMENT:
                     break;
                 case XMLStreamConstants.END_ELEMENT:
                     break loop;
-                default:
+                default: // rest is ignored
             }
         }
+        return body.toString();
     }
 
     /**
-     * Read the contents of this &lt;onentry&gt; element.
+     * Gets the Boolean attribute value at the current reader location.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param es The parent {@link EnterableState} for this onentry.
+     * @param elementName The name of the element for which the attribute value is needed.
+     * @param attrLocalName The attribute name whose value is needed.
      *
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
+     * @return The Boolean value of the attribute.
+     * @throws ModelException When the attribute value is not empty but neither "true" or "false".
      */
-    private static void readOnEntry(final XMLStreamReader reader, final Configuration configuration,
-                                    final EnterableState es)
-            throws XMLStreamException, ModelException {
-
-        final OnEntry onentry = new OnEntry();
-        onentry.setRaiseEvent(readBooleanAV(reader, SCXMLConstants.ELEM_ONENTRY, SCXMLConstants.ATTR_EVENT));
-        readExecutableContext(reader, configuration, onentry, null);
-        es.addOnEntry(onentry);
+    private static Boolean readBooleanAV(final XMLStreamReader reader, final String elementName,
+                                         final String attrLocalName)
+            throws ModelException {
+        final String value = nullIfEmpty(reader.getAttributeValue(XMLNS_DEFAULT, attrLocalName));
+        final Boolean result = "true".equals(value) ? Boolean.TRUE : "false".equals(value) ? Boolean.FALSE : null;
+        if (result == null && value != null) {
+            final MessageFormat msgFormat = new MessageFormat(ERR_ATTRIBUTE_NOT_BOOLEAN);
+            final String errMsg = msgFormat.format(new Object[] {value, attrLocalName, elementName, reader.getLocation()});
+            throw new ModelException(errMsg);
+        }
+        return result;
     }
 
     /**
-     * Read the contents of this &lt;onexit&gt; element.
+     * Read the contents of this &lt;cancel&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
-     * @param es The parent {@link EnterableState} for this onexit.
+     * @param executable The parent {@link Executable} for this action.
+     * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static void readOnExit(final XMLStreamReader reader, final Configuration configuration,
-                                   final EnterableState es)
+    private static void readCancel(final XMLStreamReader reader, final Configuration configuration,
+                                   final Executable executable, final ActionsContainer parent)
             throws XMLStreamException, ModelException {
 
-        final OnExit onexit = new OnExit();
-        onexit.setRaiseEvent(readBooleanAV(reader, SCXMLConstants.ELEM_ONEXIT, SCXMLConstants.ATTR_EVENT));
-        readExecutableContext(reader, configuration, onexit, null);
-        es.addOnExit(onexit);
+        final Cancel cancel = new Cancel();
+        cancel.setSendid(readAV(reader, SCXMLConstants.ATTR_SENDID));
+        final String attrValue = readAV(reader, SCXMLConstants.ATTR_SENDIDEXPR);
+        if (attrValue != null) {
+            if (cancel.getSendid() != null) {
+                reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_CANCEL, SCXMLConstants.ATTR_SENDID, SCXMLConstants.ATTR_SENDIDEXPR);
+            }
+            else {
+                cancel.setSendidexpr(attrValue);
+            }
+        }
+        cancel.setParent(executable);
+        if (parent != null) {
+            parent.addAction(cancel);
+        } else {
+            executable.addAction(cancel);
+        }
+        skipToEndElement(reader);
     }
 
     /**
-     * Read the contents of this simple &lt;transition&gt; element.
+     * Read the contents of this &lt;content&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
+     * @param contentContainer The {@link ContentContainer} for this content.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static SimpleTransition readSimpleTransition(final XMLStreamReader reader, final Configuration configuration)
+    private static void readContent(final XMLStreamReader reader, final Configuration configuration,
+                                    final ContentContainer contentContainer)
             throws XMLStreamException, ModelException {
 
-        final SimpleTransition transition = new SimpleTransition();
-        transition.setNext(readAV(reader, SCXMLConstants.ATTR_TARGET));
-        final String type = readAV(reader, SCXMLConstants.ATTR_TYPE);
-        if (type != null) {
-            try {
-                transition.setType(TransitionType.valueOf(type));
-            }
-            catch (final IllegalArgumentException e) {
-                final MessageFormat msgFormat = new MessageFormat(ERR_UNSUPPORTED_TRANSITION_TYPE);
-                final String errMsg = msgFormat.format(new Object[] {type, reader.getLocation()});
-                throw new ModelException(errMsg);
-            }
+        final Content content = new Content();
+        content.setExpr(readAV(reader, SCXMLConstants.ATTR_EXPR));
+        if (content.getExpr() != null) {
+            skipToEndElement(reader);
         }
-
-        readExecutableContext(reader, configuration, transition, null);
-
-        return transition;
+        else {
+            readParsedValue(reader, configuration, content, contentContainer instanceof Invoke);
+        }
+        contentContainer.setContent(content);
     }
 
     /**
-     * Read the contents of this &lt;transition&gt; element.
+     * Read the contents of this custom action.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
+     * @param customAction The {@link CustomAction} to read.
+     * @param executable The parent {@link Executable} for this custom action.
+     * @param parent The optional parent {@link ActionsContainer} if this custom action is a child of one.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static Transition readTransition(final XMLStreamReader reader, final Configuration configuration)
+    private static void readCustomAction(final XMLStreamReader reader, final Configuration configuration,
+                                         final CustomAction customAction, final Executable executable,
+                                         final ActionsContainer parent)
             throws XMLStreamException, ModelException {
 
-        final Transition transition = new Transition();
-        transition.setCond(readAV(reader, SCXMLConstants.ATTR_COND));
-        transition.setEvent(readAV(reader, SCXMLConstants.ATTR_EVENT));
-        transition.setNext(readAV(reader, SCXMLConstants.ATTR_TARGET));
-        final String type = readAV(reader, SCXMLConstants.ATTR_TYPE);
-        if (type != null) {
+        // Instantiate custom action
+        Object actionObject;
+        final String className = customAction.getActionClass().getName();
+        ClassLoader cl = configuration.customActionClassLoader;
+        if (configuration.useContextClassLoaderForCustomActions) {
+            cl = Thread.currentThread().getContextClassLoader();
+        }
+        if (cl == null) {
+            cl = SCXMLReader.class.getClassLoader();
+        }
+        Class<?> clazz;
+        try {
+            clazz = cl.loadClass(className);
+            actionObject = clazz.getConstructor().newInstance();
+        } catch (final ClassNotFoundException cnfe) {
+            throw new XMLStreamException("Cannot find custom action class:" + className, cnfe);
+        } catch (final IllegalAccessException iae) {
+            throw new XMLStreamException("Cannot access custom action class:" + className, iae);
+        } catch (final ReflectiveOperationException ie) {
+            throw new XMLStreamException("Cannot instantiate custom action class:" + className, ie);
+        }
+        if (!(actionObject instanceof Action)) {
+            throw new IllegalArgumentException(ERR_CUSTOM_ACTION_TYPE + className);
+        }
+
+        // Set the attribute values as properties
+        final Action action = (Action) actionObject;
+
+        final CustomActionWrapper actionWrapper = new CustomActionWrapper();
+        actionWrapper.setAction(action);
+        actionWrapper.setPrefix(reader.getPrefix());
+        actionWrapper.setLocalName(reader.getLocalName());
+        final Map<String, String> namespaces = readNamespaces(reader);
+        if (namespaces != null) {
+            actionWrapper.getNamespaces().putAll(namespaces);
+        }
+
+        final Map<String, String> attributes = new HashMap<>();
+        for (int i = 0; i < reader.getAttributeCount(); i++) {
+            final String name = reader.getAttributeLocalName(i);
+            final String qname = createQualifiedName(reader.getAttributePrefix(i), name);
+            final String value = reader.getAttributeValue(i);
+            attributes.put(qname, value);
+            final String setter = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
+            Method method;
             try {
-                transition.setType(TransitionType.valueOf(type));
-            }
-            catch (final IllegalArgumentException e) {
-                final MessageFormat msgFormat = new MessageFormat(ERR_UNSUPPORTED_TRANSITION_TYPE);
-                final String errMsg = msgFormat.format(new Object[] {type, reader.getLocation()});
-                throw new ModelException(errMsg);
+                method = clazz.getMethod(setter, String.class);
+                method.invoke(action, value);
+            } catch (final NoSuchMethodException nsme) {
+                logger.warn("No method: " + setter + "(String) found in custom action class: " + className+ " for "
+                        + qname + "=\"" + value + "\". Attribute ignored");
+            } catch (final InvocationTargetException ite) {
+                throw new XMLStreamException("Exception calling method:" + setter + "(String) in custom action class:"
+                        + className, ite);
+            } catch (final IllegalAccessException iae) {
+                throw new XMLStreamException("Cannot access method: " + setter +"(String) in custom action class: "
+                        + className, iae);
             }
         }
+        if (!attributes.isEmpty()) {
+            actionWrapper.setAttributes(attributes);
+        }
 
-        readExecutableContext(reader, configuration, transition, null);
+        // Add any body content if necessary
+        if (action instanceof ParsedValueContainer) {
+            readParsedValue(reader, configuration, (ParsedValueContainer)action, false);
+        }
+        else {
+            skipToEndElement(reader);
+        }
 
-        return transition;
+        // Wire in the action and add to parent
+        actionWrapper.setParent(executable);
+        if (parent != null) {
+            parent.addAction(actionWrapper);
+        } else {
+            executable.addAction(actionWrapper);
+        }
     }
 
     /**
-     * Read this set of executable content elements.
+     * Read the contents of this &lt;data&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
-     * @param executable The parent {@link Executable} to which this content belongs.
-     * @param parent The optional parent {@link ActionsContainer} if this is child content of an ActionsContainer action.
+     * @param dm The parent {@link Datamodel} for this data.
+     *
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     */
+    private static void readData(final XMLStreamReader reader, final Configuration configuration, final Datamodel dm)
+            throws XMLStreamException, ModelException {
+
+        final Data datum = new Data();
+        datum.setId(readRequiredAV(reader, SCXMLConstants.ELEM_DATA, SCXMLConstants.ATTR_ID));
+        final String expr = readAV(reader, SCXMLConstants.ATTR_EXPR);
+        final String src = readAV(reader, SCXMLConstants.ATTR_SRC);
+
+        if (expr != null) {
+            if (src != null) {
+                reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_DATA, SCXMLConstants.ATTR_EXPR, SCXMLConstants.ATTR_SRC);
+            }
+            datum.setExpr(expr);
+            skipToEndElement(reader);
+        } else if (src != null ) {
+            datum.setSrc(src);
+            skipToEndElement(reader);
+        } else {
+            readParsedValue(reader, configuration, datum, false);
+        }
+        dm.addData(datum);
+    }
+
+    /**
+     * Read the contents of this &lt;datamodel&gt; element.
+     *
+     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
+     * @param configuration The {@link Configuration} to use while parsing.
+     * @param scxml The root of the object model being parsed.
+     * @param parent The parent {@link TransitionalState} for this datamodel (null for top level).
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
      *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static void readExecutableContext(final XMLStreamReader reader, final Configuration configuration,
-                                              final Executable executable, final ActionsContainer parent)
+    private static void readDatamodel(final XMLStreamReader reader, final Configuration configuration,
+                                      final SCXML scxml, final TransitionalState parent)
             throws XMLStreamException, ModelException {
 
-        String end = "";
-        if (parent != null) {
-            end = parent instanceof If ? SCXMLConstants.ELEM_IF : SCXMLConstants.ELEM_FOREACH;
-        } else if (executable instanceof SimpleTransition) {
-            end = SCXMLConstants.ELEM_TRANSITION;
-        } else if (executable instanceof OnEntry) {
-            end = SCXMLConstants.ELEM_ONENTRY;
-        } else if (executable instanceof OnExit) {
-            end = SCXMLConstants.ELEM_ONEXIT;
-        } else if (executable instanceof Finalize) {
-            end = SCXMLConstants.ELEM_FINALIZE;
-        }
+        final Datamodel dm = new Datamodel();
 
         loop : while (reader.hasNext()) {
             String name, nsURI;
@@ -1464,58 +1259,13 @@ public final class SCXMLReader {
                     nsURI = reader.getNamespaceURI();
                     name = reader.getLocalName();
                     if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_RAISE.equals(name)) {
-                            if (executable instanceof Finalize) {
-                                reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_FINALIZE, nsURI, name);
-                            } else {
-                                readRaise(reader, configuration, executable, parent);
-                            }
-                        } else if (SCXMLConstants.ELEM_FOREACH.equals(name)) {
-                            readForeach(reader, configuration, executable, parent);
-                        } else if (SCXMLConstants.ELEM_IF.equals(name)) {
-                            readIf(reader, configuration, executable, parent);
-                        } else if (SCXMLConstants.ELEM_LOG.equals(name)) {
-                            readLog(reader, executable, parent);
-                        } else if (SCXMLConstants.ELEM_ASSIGN.equals(name)) {
-                            readAssign(reader, configuration, executable, parent);
-                        } else if (SCXMLConstants.ELEM_SEND.equals(name)) {
-                            if (executable instanceof Finalize) {
-                                reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_FINALIZE, nsURI, name);
-                            } else {
-                                readSend(reader, configuration, executable, parent);
-                            }
-                        } else if (SCXMLConstants.ELEM_CANCEL.equals(name)) {
-                            readCancel(reader, configuration, executable, parent);
-                        } else if (SCXMLConstants.ELEM_SCRIPT.equals(name)) {
-                            readScript(reader, configuration, executable, parent);
-                        } else if (SCXMLConstants.ELEM_IF.equals(end) && SCXMLConstants.ELEM_ELSEIF.equals(name)) {
-                            readElseIf(reader, executable, (If) parent);
-                        } else if (SCXMLConstants.ELEM_IF.equals(end) && SCXMLConstants.ELEM_ELSE.equals(name)) {
-                            readElse(reader, executable, (If)parent);
-                        } else {
-                            reportIgnoredElement(reader, configuration, end, nsURI, name);
-                        }
-                    } else if (SCXMLConstants.XMLNS_COMMONS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_VAR.equals(name)) {
-                            readCustomAction(reader, configuration, Var.CUSTOM_ACTION, executable, parent);
-                        } else {
-                            reportIgnoredElement(reader, configuration, end, nsURI, name);
-                        }
-                    } else { // custom action
-                        CustomAction customAction = null;
-                        if (!configuration.customActions.isEmpty()) {
-                            for (final CustomAction ca : configuration.customActions) {
-                                if (ca.getNamespaceURI().equals(nsURI) && ca.getLocalName().equals(name)) {
-                                    customAction = ca;
-                                    break;
-                                }
-                            }
-                        }
-                        if (customAction != null) {
-                            readCustomAction(reader, configuration, customAction, executable, parent);
+                        if (SCXMLConstants.ELEM_DATA.equals(name)) {
+                            readData(reader, configuration, dm);
                         } else {
-                            reportIgnoredElement(reader, configuration, end, nsURI, name);
+                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_DATAMODEL, nsURI, name);
                         }
+                    } else {
+                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_DATAMODEL, nsURI, name);
                     }
                     break;
                 case XMLStreamConstants.END_ELEMENT:
@@ -1523,87 +1273,196 @@ public final class SCXMLReader {
                 default:
             }
         }
+
+        if (parent == null) {
+            scxml.setDatamodel(dm);
+        } else {
+            parent.setDatamodel(dm);
+        }
     }
 
+    /*
+     * Private utility functions for reading the SCXML document.
+     */
     /**
-     * Read the contents of this &lt;raise&gt; element.
+     * Read the SCXML document through the {@link XMLStreamReader}.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
-     * @param executable The parent {@link Executable} for this action.
-     * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
      *
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document
+     *         (not wired up to be immediately usable).
+     *
+     * @throws IOException An IO error during parsing.
      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
      *                        errors in the SCXML document that may not be identified by the schema).
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static void readRaise(final XMLStreamReader reader, final Configuration configuration,
-                                  final Executable executable, final ActionsContainer parent)
-            throws XMLStreamException, ModelException {
+    private static SCXML readDocument(final XMLStreamReader reader, final Configuration configuration)
+            throws IOException, ModelException, XMLStreamException {
 
-        if (executable instanceof Finalize) {
-            // https://www.w3.org/TR/2015/REC-scxml-20150901/#finalize
-            // [...] the executable content inside <finalize> MUST NOT raise events or invoke external actions.
-            // In particular, the <send> and <raise> elements MUST NOT occur.
-            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_FINALIZE, SCXMLConstants.XMLNS_SCXML, SCXMLConstants.ELEM_RAISE);
-        }
-        else {
-            final Raise raise = new Raise();
-            raise.setEvent(readAV(reader, SCXMLConstants.ATTR_EVENT));
-            raise.setParent(executable);
-            if (parent != null) {
-                parent.addAction(raise);
-            } else {
-                executable.addAction(raise);
+        final SCXML scxml = new SCXML();
+        scxml.setPathResolver(configuration.pathResolver);
+        while (reader.hasNext()) {
+            String name, nsURI;
+            switch (reader.next()) {
+                case XMLStreamConstants.START_ELEMENT:
+                    nsURI = reader.getNamespaceURI();
+                    name = reader.getLocalName();
+                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
+                        if (SCXMLConstants.ELEM_SCXML.equals(name)) {
+                            readSCXML(reader, configuration, scxml);
+                        } else {
+                            reportIgnoredElement(reader, configuration, "DOCUMENT_ROOT", nsURI, name);
+                        }
+                    } else {
+                        reportIgnoredElement(reader, configuration, "DOCUMENT_ROOT", nsURI, name);
+                    }
+                    break;
+                case XMLStreamConstants.NAMESPACE:
+                    System.err.println(reader.getNamespaceCount());
+                    break;
+                default:
             }
-            skipToEndElement(reader);
         }
+        return scxml;
     }
 
     /**
-     * Read the contents of this &lt;if&gt; element.
+     * Read the contents of this &lt;donedata&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
-     * @param executable The parent {@link Executable} for this action.
-     * @param parent The optional parent {@link ActionsContainer} if this &lt;if&gt; is a child of one.
+     * @param parent The parent {@link State} for this final (null for top level state).
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
      *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static void readIf(final XMLStreamReader reader, final Configuration configuration,
-                               final Executable executable, final ActionsContainer parent)
+    private static void readDoneData(final XMLStreamReader reader, final Configuration configuration, final Final parent)
             throws XMLStreamException, ModelException {
 
-        final If iff = new If();
-        iff.setCond(readRequiredAV(reader, SCXMLConstants.ELEM_IF, SCXMLConstants.ATTR_COND));
-        iff.setParent(executable);
-        if (parent != null) {
-            parent.addAction(iff);
-        } else {
-            executable.addAction(iff);
+        final DoneData doneData = new DoneData();
+        parent.setDoneData(doneData);
+
+        loop : while (reader.hasNext()) {
+            String name, nsURI;
+            switch (reader.next()) {
+                case XMLStreamConstants.START_ELEMENT:
+                    nsURI = reader.getNamespaceURI();
+                    name = reader.getLocalName();
+                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
+                        if (SCXMLConstants.ELEM_PARAM.equals(name)) {
+                            if (doneData.getContent() == null) {
+                                readParam(reader, configuration, doneData);
+                            }
+                            else {
+                                reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_DONEDATA, nsURI, name);
+                            }
+                        } else if (SCXMLConstants.ELEM_CONTENT.equals(name)) {
+                            if (doneData.getParams().isEmpty()) {
+                                readContent(reader, configuration, doneData);
+                            }
+                            else {
+                                reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_DONEDATA, nsURI, name);
+                            }
+                        } else {
+                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_DONEDATA, nsURI, name);
+                        }
+                    } else {
+                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_DONEDATA, nsURI, name);
+                    }
+                    break;
+                case XMLStreamConstants.END_ELEMENT:
+                    break loop;
+                default:
+            }
         }
-        readExecutableContext(reader, configuration, executable, iff);
     }
 
     /**
-     * Read the contents of this &lt;elseif&gt; element.
+     * Read the current element into a DOM {@link Element}.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param executable The parent {@link Executable} for this action.
-     * @param iff The parent {@link If} for this &lt;elseif&gt;.
+     *
+     * @return The parsed content as a DOM {@link Element}.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static void readElseIf(final XMLStreamReader reader, final Executable executable, final If iff)
-            throws XMLStreamException, ModelException {
+    private static Element readElement(final XMLStreamReader reader)
+            throws XMLStreamException {
 
-        final ElseIf elseif = new ElseIf();
-        elseif.setCond(readRequiredAV(reader, SCXMLConstants.ELEM_ELSEIF, SCXMLConstants.ATTR_COND));
-        elseif.setParent(executable);
-        iff.addAction(elseif);
-        skipToEndElement(reader);
+        // Create a document in which to build the DOM node
+        Document document;
+        try {
+            document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+        } catch (final ParserConfigurationException pce) {
+            throw new XMLStreamException(ERR_PARSER_CFG);
+        }
+
+        // This root element will be returned, add any attributes as specified
+        final Element root = document.createElementNS(reader.getNamespaceURI(), createQualifiedName(reader.getPrefix(), reader.getLocalName()));
+        document.appendChild(root);
+
+        boolean children = false, cdata = false;
+        Node parent = root;
+
+        // Convert stream to DOM node(s) while maintaining parent child relationships
+        loop : while (reader.hasNext()) {
+            Node child = null;
+            switch (reader.next()) {
+                case XMLStreamConstants.START_ELEMENT:
+                    if (!children && root.hasChildNodes()) {
+                        // remove any children
+                        root.setTextContent(null);
+                    }
+                    children = true;
+                    final Element elem = document.createElementNS(reader.getNamespaceURI(),
+                            createQualifiedName(reader.getPrefix(), reader.getLocalName()));
+                    for (int i = 0; i < reader.getAttributeCount(); i++) {
+                        final Attr attr = document.createAttributeNS(reader.getAttributeNamespace(i),
+                                createQualifiedName(reader.getAttributePrefix(i), reader.getAttributeLocalName(i)));
+                        attr.setValue(reader.getAttributeValue(i));
+                        elem.setAttributeNodeNS(attr);
+                    }
+                    parent.appendChild(elem);
+                    parent = elem;
+                    break;
+                case XMLStreamConstants.SPACE:
+                case XMLStreamConstants.CHARACTERS:
+                case XMLStreamConstants.ENTITY_REFERENCE:
+                    if (!children || parent != root) {
+                        child = document.createTextNode(reader.getText());
+                    }
+                    break;
+                case XMLStreamConstants.CDATA:
+                    if (!children || parent != root) {
+                        cdata = true;
+                        child = document.createCDATASection(reader.getText());
+                    }
+                    break;
+                case XMLStreamConstants.END_ELEMENT:
+                    parent = parent.getParentNode();
+                    if (parent == document) {
+                        break loop;
+                    }
+                    break;
+                default: // rest is ignored
+            }
+            if (child != null) {
+                parent.appendChild(child);
+            }
+        }
+        if (!children && root.hasChildNodes() && root.getChildNodes().getLength() > 1) {
+            final String text = root.getTextContent().trim();
+            if (!cdata) {
+                root.setTextContent(text);
+            } else {
+                root.setTextContent(null);
+                root.appendChild(document.createCDATASection(text));
+            }
+        }
+        return root;
     }
 
     /**
@@ -1625,187 +1484,51 @@ public final class SCXMLReader {
     }
 
     /**
-     * Read the contents of this &lt;foreach&gt; element.
+     * Read the contents of this &lt;elseif&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
      * @param executable The parent {@link Executable} for this action.
-     * @param parent The optional parent {@link ActionsContainer} if this &lt;foreach&gt; is a child of one.
+     * @param iff The parent {@link If} for this &lt;elseif&gt;.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static void readForeach(final XMLStreamReader reader, final Configuration configuration,
-                                    final Executable executable, final ActionsContainer parent)
+    private static void readElseIf(final XMLStreamReader reader, final Executable executable, final If iff)
             throws XMLStreamException, ModelException {
 
-        final Foreach fe = new Foreach();
-        fe.setArray(readRequiredAV(reader, SCXMLConstants.ELEM_FOREACH, SCXMLConstants.ATTR_ARRAY));
-        fe.setItem(readRequiredAV(reader, SCXMLConstants.ELEM_FOREACH, SCXMLConstants.ATTR_ITEM));
-        fe.setIndex(readAV(reader, SCXMLConstants.ATTR_INDEX));
-        fe.setParent(executable);
-        if (parent != null) {
-            parent.addAction(fe);
-        } else {
-            executable.addAction(fe);
-        }
-        readExecutableContext(reader, configuration, executable, fe);
-    }
-
-    /**
-     * Read the contents of this &lt;log&gt; element.
-     *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param executable The parent {@link Executable} for this action.
-     * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
-     *
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     */
-    private static void readLog(final XMLStreamReader reader, final Executable executable, final ActionsContainer parent)
-            throws XMLStreamException {
-
-        final Log log = new Log();
-        log.setExpr(readAV(reader, SCXMLConstants.ATTR_EXPR));
-        log.setLabel(readAV(reader, SCXMLConstants.ATTR_LABEL));
-        log.setParent(executable);
-        if (parent != null) {
-            parent.addAction(log);
-        } else {
-            executable.addAction(log);
-        }
+        final ElseIf elseif = new ElseIf();
+        elseif.setCond(readRequiredAV(reader, SCXMLConstants.ELEM_ELSEIF, SCXMLConstants.ATTR_COND));
+        elseif.setParent(executable);
+        iff.addAction(elseif);
         skipToEndElement(reader);
     }
 
     /**
-     * Read the contents of this &lt;assign&gt; element.
+     * Read this set of executable content elements.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
-     * @param executable The parent {@link Executable} for this action.
-     * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
+     * @param executable The parent {@link Executable} to which this content belongs.
+     * @param parent The optional parent {@link ActionsContainer} if this is child content of an ActionsContainer action.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static void readAssign(final XMLStreamReader reader, final Configuration configuration,
-                                   final Executable executable, final ActionsContainer parent)
+    private static void readExecutableContext(final XMLStreamReader reader, final Configuration configuration,
+                                              final Executable executable, final ActionsContainer parent)
             throws XMLStreamException, ModelException {
 
-        final Assign assign = new Assign();
-        assign.setExpr(readAV(reader, SCXMLConstants.ATTR_EXPR));
-        assign.setLocation(readRequiredAV(reader, SCXMLConstants.ELEM_ASSIGN, SCXMLConstants.ATTR_LOCATION));
-        assign.setSrc(readAV(reader, SCXMLConstants.ATTR_SRC));
-        if (assign.getExpr() != null && assign.getSrc() != null) {
-            reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_ASSIGN, SCXMLConstants.ATTR_EXPR, SCXMLConstants.ATTR_SRC);
-        }
-        else if (assign.getExpr() != null) {
-            skipToEndElement(reader);
-        }
-        else if (assign.getSrc() != null) {
-            skipToEndElement(reader);
-            String resolvedSrc = assign.getSrc();
-            if (configuration.pathResolver != null) {
-                resolvedSrc = configuration.pathResolver.resolvePath(resolvedSrc);
-            }
-            try {
-                assign.setParsedValue(ContentParser.DEFAULT_PARSER.parseResource(resolvedSrc));
-            } catch (final IOException e) {
-                throw new ModelException(e);
-            }
-        }
-        else {
-            final Location location = reader.getLocation();
-            readParsedValue(reader, configuration, assign, false);
-            if (assign.getParsedValue() == null) {
-                // report missing expression (as most common use-case)
-                reportMissingAttribute(location, SCXMLConstants.ELEM_ASSIGN, SCXMLConstants.ATTR_EXPR);
-            }
-        }
-
-        assign.setParent(executable);
+        String end = "";
         if (parent != null) {
-            parent.addAction(assign);
-        } else {
-            executable.addAction(assign);
-        }
-    }
-
-    /**
-     * Read the contents of this &lt;send&gt; element.
-     *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param configuration The {@link Configuration} to use while parsing.
-     * @param executable The parent {@link Executable} for this action.
-     * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
-     *
-     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
-     */
-    private static void readSend(final XMLStreamReader reader, final Configuration configuration,
-                                 final Executable executable, final ActionsContainer parent)
-            throws XMLStreamException, ModelException {
-
-        if (executable instanceof Finalize) {
-            // https://www.w3.org/TR/2015/REC-scxml-20150901/#finalize
-            // [...] the executable content inside <finalize> MUST NOT raise events or invoke external actions.
-            // In particular, the <send> and <raise> elements MUST NOT occur.
-            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_FINALIZE, SCXMLConstants.XMLNS_SCXML, SCXMLConstants.ELEM_SEND);
-            return;
-        }
-
-        final Send send = new Send();
-        send.setId(readAV(reader, SCXMLConstants.ATTR_ID));
-        String attrValue = readAV(reader, SCXMLConstants.ATTR_IDLOCATION);
-        if (attrValue != null) {
-            if (send.getId() != null) {
-                reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_SEND, SCXMLConstants.ATTR_ID, SCXMLConstants.ATTR_IDLOCATION);
-            }
-            else {
-                send.setIdlocation(attrValue);
-            }
-        }
-        send.setDelay(readAV(reader, SCXMLConstants.ATTR_DELAY));
-        attrValue = readAV(reader, SCXMLConstants.ATTR_DELAYEXPR);
-        if (attrValue != null) {
-            if (send.getDelay() != null) {
-                reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_SEND, SCXMLConstants.ATTR_DELAY, SCXMLConstants.ATTR_DELAYEXPR);
-            }
-            else {
-                send.setDelayexpr(attrValue);
-            }
-        }
-        send.setEvent(readAV(reader, SCXMLConstants.ATTR_EVENT));
-        attrValue = readAV(reader, SCXMLConstants.ATTR_EVENTEXPR);
-        if (attrValue != null) {
-            if (send.getEvent() != null) {
-                reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_SEND, SCXMLConstants.ATTR_EVENT, SCXMLConstants.ATTR_EVENTEXPR);
-            }
-            else {
-                send.setEventexpr(attrValue);
-            }
-        }
-        send.setHints(readAV(reader, SCXMLConstants.ATTR_HINTS));
-        send.setNamelist(readAV(reader, SCXMLConstants.ATTR_NAMELIST));
-        send.setTarget(readAV(reader, SCXMLConstants.ATTR_TARGET));
-        attrValue = readAV(reader, SCXMLConstants.ATTR_TARGETEXPR);
-        if (attrValue != null) {
-            if (send.getTarget() != null) {
-                reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_SEND, SCXMLConstants.ATTR_TARGET, SCXMLConstants.ATTR_TARGETEXPR);
-            }
-            else {
-                send.setTargetexpr(attrValue);
-            }
-        }
-        send.setType(readAV(reader, SCXMLConstants.ATTR_TYPE));
-        attrValue = readAV(reader, SCXMLConstants.ATTR_TYPEEXPR);
-        if (attrValue != null) {
-            if (send.getType() != null) {
-                reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_SEND, SCXMLConstants.ATTR_TYPE, SCXMLConstants.ATTR_TYPEEXPR);
-            }
-            else {
-                send.setTypeexpr(attrValue);
-            }
+            end = parent instanceof If ? SCXMLConstants.ELEM_IF : SCXMLConstants.ELEM_FOREACH;
+        } else if (executable instanceof SimpleTransition) {
+            end = SCXMLConstants.ELEM_TRANSITION;
+        } else if (executable instanceof OnEntry) {
+            end = SCXMLConstants.ELEM_ONENTRY;
+        } else if (executable instanceof OnExit) {
+            end = SCXMLConstants.ELEM_ONEXIT;
+        } else if (executable instanceof Finalize) {
+            end = SCXMLConstants.ELEM_FINALIZE;
         }
 
         loop : while (reader.hasNext()) {
@@ -1815,25 +1538,58 @@ public final class SCXMLReader {
                     nsURI = reader.getNamespaceURI();
                     name = reader.getLocalName();
                     if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
-                        if (SCXMLConstants.ELEM_PARAM.equals(name)) {
-                            if (send.getContent() == null) {
-                                readParam(reader, configuration, send);
-                            }
-                            else {
-                                reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_SEND, nsURI, name);
+                        if (SCXMLConstants.ELEM_RAISE.equals(name)) {
+                            if (executable instanceof Finalize) {
+                                reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_FINALIZE, nsURI, name);
+                            } else {
+                                readRaise(reader, configuration, executable, parent);
                             }
-                        } else if (SCXMLConstants.ELEM_CONTENT.equals(name)) {
-                            if (send.getNamelist() == null && send.getParams().isEmpty()) {
-                                readContent(reader, configuration, send);
+                        } else if (SCXMLConstants.ELEM_FOREACH.equals(name)) {
+                            readForeach(reader, configuration, executable, parent);
+                        } else if (SCXMLConstants.ELEM_IF.equals(name)) {
+                            readIf(reader, configuration, executable, parent);
+                        } else if (SCXMLConstants.ELEM_LOG.equals(name)) {
+                            readLog(reader, executable, parent);
+                        } else if (SCXMLConstants.ELEM_ASSIGN.equals(name)) {
+                            readAssign(reader, configuration, executable, parent);
+                        } else if (SCXMLConstants.ELEM_SEND.equals(name)) {
+                            if (executable instanceof Finalize) {
+                                reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_FINALIZE, nsURI, name);
+                            } else {
+                                readSend(reader, configuration, executable, parent);
                             }
-                            else {
-                                reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_SEND, nsURI, name);
+                        } else if (SCXMLConstants.ELEM_CANCEL.equals(name)) {
+                            readCancel(reader, configuration, executable, parent);
+                        } else if (SCXMLConstants.ELEM_SCRIPT.equals(name)) {
+                            readScript(reader, configuration, executable, parent);
+                        } else if (SCXMLConstants.ELEM_IF.equals(end) && SCXMLConstants.ELEM_ELSEIF.equals(name)) {
+                            readElseIf(reader, executable, (If) parent);
+                        } else if (SCXMLConstants.ELEM_IF.equals(end) && SCXMLConstants.ELEM_ELSE.equals(name)) {
+                            readElse(reader, executable, (If)parent);
+                        } else {
+                            reportIgnoredElement(reader, configuration, end, nsURI, name);
+                        }
+                    } else if (SCXMLConstants.XMLNS_COMMONS_SCXML.equals(nsURI)) {
+                        if (SCXMLConstants.ELEM_VAR.equals(name)) {
+                            readCustomAction(reader, configuration, Var.CUSTOM_ACTION, executable, parent);
+                        } else {
+                            reportIgnoredElement(reader, configuration, end, nsURI, name);
+                        }
+                    } else { // custom action
+                        CustomAction customAction = null;
+                        if (!configuration.customActions.isEmpty()) {
+                            for (final CustomAction ca : configuration.customActions) {
+                                if (ca.getNamespaceURI().equals(nsURI) && ca.getLocalName().equals(name)) {
+                                    customAction = ca;
+                                    break;
+                                }
                             }
+                        }
+                        if (customAction != null) {
+                            readCustomAction(reader, configuration, customAction, executable, parent);
                         } else {
-                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_SEND, nsURI, name);
+                            reportIgnoredElement(reader, configuration, end, nsURI, name);
                         }
-                    } else {
-                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_SEND, nsURI, name);
                     }
                     break;
                 case XMLStreamConstants.END_ELEMENT:
@@ -1841,70 +1597,115 @@ public final class SCXMLReader {
                 default:
             }
         }
+    }
 
-        send.setParent(executable);
-        if (parent != null) {
-            parent.addAction(send);
+    /**
+     * Read the contents of this &lt;final&gt; element.
+     *
+     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
+     * @param configuration The {@link Configuration} to use while parsing.
+     * @param scxml The root of the object model being parsed.
+     * @param parent The parent {@link State} for this final (null for top level state).
+     *
+     * @throws IOException An IO error during parsing.
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
+     */
+    private static void readFinal(final XMLStreamReader reader, final Configuration configuration, final SCXML scxml,
+                                  final State parent)
+            throws XMLStreamException, ModelException, IOException {
+
+        final Final end = new Final();
+        end.setId(readOrGeneratedTransitionTargetId(reader, scxml, SCXMLConstants.ELEM_FINAL));
+
+        if (parent == null) {
+            scxml.addChild(end);
         } else {
-            executable.addAction(send);
+            parent.addChild(end);
+        }
+
+        scxml.addTarget(end);
+        if (configuration.parent != null) {
+            configuration.parent.addTarget(end);
+        }
+
+        loop : while (reader.hasNext()) {
+            String name, nsURI;
+            switch (reader.next()) {
+                case XMLStreamConstants.START_ELEMENT:
+                    nsURI = reader.getNamespaceURI();
+                    name = reader.getLocalName();
+                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
+                        if (SCXMLConstants.ELEM_ONENTRY.equals(name)) {
+                            readOnEntry(reader, configuration, end);
+                        } else if (SCXMLConstants.ELEM_ONEXIT.equals(name)) {
+                            readOnExit(reader, configuration, end);
+                        } else if (SCXMLConstants.ELEM_DONEDATA.equals(name) && end.getDoneData() == null) {
+                            readDoneData(reader, configuration, end);
+                        } else {
+                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_FINAL, nsURI, name);
+                        }
+                    } else {
+                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_FINAL, nsURI, name);
+                    }
+                    break;
+                case XMLStreamConstants.END_ELEMENT:
+                    break loop;
+                default:
+            }
         }
     }
 
     /**
-     * Read the contents of this &lt;cancel&gt; element.
+     * Read the contents of this &lt;finalize&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
-     * @param executable The parent {@link Executable} for this action.
-     * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
+     * @param state The {@link TransitionalState} which contains the parent {@link Invoke}.
+     * @param invoke The parent {@link Invoke} for this finalize.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static void readCancel(final XMLStreamReader reader, final Configuration configuration,
-                                   final Executable executable, final ActionsContainer parent)
+    private static void readFinalize(final XMLStreamReader reader, final Configuration configuration,
+                                     final TransitionalState state, final Invoke invoke)
             throws XMLStreamException, ModelException {
 
-        final Cancel cancel = new Cancel();
-        cancel.setSendid(readAV(reader, SCXMLConstants.ATTR_SENDID));
-        final String attrValue = readAV(reader, SCXMLConstants.ATTR_SENDIDEXPR);
-        if (attrValue != null) {
-            if (cancel.getSendid() != null) {
-                reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_CANCEL, SCXMLConstants.ATTR_SENDID, SCXMLConstants.ATTR_SENDIDEXPR);
-            }
-            else {
-                cancel.setSendidexpr(attrValue);
-            }
-        }
-        cancel.setParent(executable);
-        if (parent != null) {
-            parent.addAction(cancel);
-        } else {
-            executable.addAction(cancel);
-        }
-        skipToEndElement(reader);
+        final Finalize finalize = new Finalize();
+        readExecutableContext(reader, configuration, finalize, null);
+        invoke.setFinalize(finalize);
+        finalize.setParent(state);
     }
 
     /**
-     * Read the contents of this &lt;script&gt; element.
+     * Read the contents of this &lt;foreach&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
      * @param executable The parent {@link Executable} for this action.
-     * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
+     * @param parent The optional parent {@link ActionsContainer} if this &lt;foreach&gt; is a child of one.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static void readScript(final XMLStreamReader reader, final Configuration configuration,
-                                   final Executable executable, final ActionsContainer parent)
+    private static void readForeach(final XMLStreamReader reader, final Configuration configuration,
+                                    final Executable executable, final ActionsContainer parent)
             throws XMLStreamException, ModelException {
 
-        final Script script = readScript(reader, configuration);
-        script.setParent(executable);
+        final Foreach fe = new Foreach();
+        fe.setArray(readRequiredAV(reader, SCXMLConstants.ELEM_FOREACH, SCXMLConstants.ATTR_ARRAY));
+        fe.setItem(readRequiredAV(reader, SCXMLConstants.ELEM_FOREACH, SCXMLConstants.ATTR_ITEM));
+        fe.setIndex(readAV(reader, SCXMLConstants.ATTR_INDEX));
+        fe.setParent(executable);
         if (parent != null) {
-            parent.addAction(script);
+            parent.addAction(fe);
         } else {
-            executable.addAction(script);
+            executable.addAction(fe);
         }
+        readExecutableContext(reader, configuration, executable, fe);
     }
 
     /**
@@ -1928,395 +1729,310 @@ public final class SCXMLReader {
     }
 
     /**
-     * Read the contents of this &lt;script&gt; element.
+     * Read the contents of this &lt;history&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
+     * @param scxml The root of the object model being parsed.
+     * @param ts The parent {@link org.apache.commons.scxml2.model.TransitionalState} for this history.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static Script readScript(final XMLStreamReader reader, final Configuration configuration)
+    private static void readHistory(final XMLStreamReader reader, final Configuration configuration,
+                                    final SCXML scxml, final TransitionalState ts)
             throws XMLStreamException, ModelException {
 
-        final Script script = new Script();
-        script.setSrc(readAV(reader, SCXMLConstants.ATTR_SRC));
-        if (script.getSrc() != null) {
-            String resolvedSrc = script.getSrc();
-            if (configuration.pathResolver != null) {
-                resolvedSrc = configuration.pathResolver.resolvePath(resolvedSrc);
-            }
-            try (InputStream in = new URL(resolvedSrc).openStream()){
-                script.setScript(IOUtils.toString(in, "UTF-8"));
-            }
-            catch (final IOException e) {
-                throw new ModelException(e);
+        final History history = new History();
+        history.setId(readOrGeneratedTransitionTargetId(reader, scxml, SCXMLConstants.ELEM_HISTORY));
+        history.setType(readAV(reader, SCXMLConstants.ATTR_TYPE));
+
+        ts.addHistory(history);
+        scxml.addTarget(history);
+
+        loop : while (reader.hasNext()) {
+            String name, nsURI;
+            switch (reader.next()) {
+                case XMLStreamConstants.START_ELEMENT:
+                    nsURI = reader.getNamespaceURI();
+                    name = reader.getLocalName();
+                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
+                        if (SCXMLConstants.ELEM_TRANSITION.equals(name)) {
+                            history.setTransition(readTransition(reader, configuration));
+                        } else {
+                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_HISTORY, nsURI, name);
+                        }
+                    } else {
+                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_HISTORY, nsURI, name);
+                    }
+                    break;
+                case XMLStreamConstants.END_ELEMENT:
+                    break loop;
+                default:
             }
-            skipToEndElement(reader);
-        }
-        else {
-            script.setScript(readBody(reader));
         }
-        return script;
     }
 
     /**
-     * Read the contents of this custom action.
+     * Read the contents of this &lt;if&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
-     * @param customAction The {@link CustomAction} to read.
-     * @param executable The parent {@link Executable} for this custom action.
-     * @param parent The optional parent {@link ActionsContainer} if this custom action is a child of one.
+     * @param executable The parent {@link Executable} for this action.
+     * @param parent The optional parent {@link ActionsContainer} if this &lt;if&gt; is a child of one.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static void readCustomAction(final XMLStreamReader reader, final Configuration configuration,
-                                         final CustomAction customAction, final Executable executable,
-                                         final ActionsContainer parent)
+    private static void readIf(final XMLStreamReader reader, final Configuration configuration,
+                               final Executable executable, final ActionsContainer parent)
             throws XMLStreamException, ModelException {
 
-        // Instantiate custom action
-        Object actionObject;
-        final String className = customAction.getActionClass().getName();
-        ClassLoader cl = configuration.customActionClassLoader;
-        if (configuration.useContextClassLoaderForCustomActions) {
-            cl = Thread.currentThread().getContextClassLoader();
-        }
-        if (cl == null) {
-            cl = SCXMLReader.class.getClassLoader();
-        }
-        Class<?> clazz;
-        try {
-            clazz = cl.loadClass(className);
-            actionObject = clazz.getConstructor().newInstance();
-        } catch (final ClassNotFoundException cnfe) {
-            throw new XMLStreamException("Cannot find custom action class:" + className, cnfe);
-        } catch (final IllegalAccessException iae) {
-            throw new XMLStreamException("Cannot access custom action class:" + className, iae);
-        } catch (final ReflectiveOperationException ie) {
-            throw new XMLStreamException("Cannot instantiate custom action class:" + className, ie);
-        }
-        if (!(actionObject instanceof Action)) {
-            throw new IllegalArgumentException(ERR_CUSTOM_ACTION_TYPE + className);
-        }
-
-        // Set the attribute values as properties
-        final Action action = (Action) actionObject;
-
-        final CustomActionWrapper actionWrapper = new CustomActionWrapper();
-        actionWrapper.setAction(action);
-        actionWrapper.setPrefix(reader.getPrefix());
-        actionWrapper.setLocalName(reader.getLocalName());
-        final Map<String, String> namespaces = readNamespaces(reader);
-        if (namespaces != null) {
-            actionWrapper.getNamespaces().putAll(namespaces);
+        final If iff = new If();
+        iff.setCond(readRequiredAV(reader, SCXMLConstants.ELEM_IF, SCXMLConstants.ATTR_COND));
+        iff.setParent(executable);
+        if (parent != null) {
+            parent.addAction(iff);
+        } else {
+            executable.addAction(iff);
         }
+        readExecutableContext(reader, configuration, executable, iff);
+    }
 
-        final Map<String, String> attributes = new HashMap<>();
-        for (int i = 0; i < reader.getAttributeCount(); i++) {
-            final String name = reader.getAttributeLocalName(i);
-            final String qname = createQualifiedName(reader.getAttributePrefix(i), name);
-            final String value = reader.getAttributeValue(i);
-            attributes.put(qname, value);
-            final String setter = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
-            Method method;
-            try {
-                method = clazz.getMethod(setter, String.class);
-                method.invoke(action, value);
-            } catch (final NoSuchMethodException nsme) {
-                logger.warn("No method: " + setter + "(String) found in custom action class: " + className+ " for "
-                        + qname + "=\"" + value + "\". Attribute ignored");
-            } catch (final InvocationTargetException ite) {
-                throw new XMLStreamException("Exception calling method:" + setter + "(String) in custom action class:"
-                        + className, ite);
-            } catch (final IllegalAccessException iae) {
-                throw new XMLStreamException("Cannot access method: " + setter +"(String) in custom action class: "
-                        + className, iae);
+    /**
+     * Add all the nested targets from given target to given parent state machine.
+     *
+     * @param parent The state machine
+     * @param es The target to import
+     */
+    private static void readInExternalTargets(final SCXML parent, final EnterableState es) {
+        if (es instanceof TransitionalState) {
+            for (final History h : ((TransitionalState)es).getHistory()) {
+                parent.addTarget(h);
+            }
+            for (final EnterableState child : ((TransitionalState) es).getChildren()) {
+                parent.addTarget(child);
+                readInExternalTargets(parent, child);
             }
-        }
-        if (!attributes.isEmpty()) {
-            actionWrapper.setAttributes(attributes);
-        }
-
-        // Add any body content if necessary
-        if (action instanceof ParsedValueContainer) {
-            readParsedValue(reader, configuration, (ParsedValueContainer)action, false);
-        }
-        else {
-            skipToEndElement(reader);
-        }
-
-        // Wire in the action and add to parent
-        actionWrapper.setParent(executable);
-        if (parent != null) {
-            parent.addAction(actionWrapper);
-        } else {
-            executable.addAction(actionWrapper);
         }
     }
 
     /**
-     * Read and parse the body of a {@link ParsedValueContainer} element.
+     * Read the contents of this &lt;initial&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
-     * @param valueContainer The {@link ParsedValueContainer} element which body to read
-     * @param invokeContent flag indicating if the valueContainer is a <invoke><content> element, which get special
-     *                      treatment
+     * @param state The parent composite {@link State} for this initial.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static void readParsedValue(final XMLStreamReader reader, final Configuration configuration,
-                                        final ParsedValueContainer valueContainer, final boolean invokeContent)
+    private static void readInitial(final XMLStreamReader reader, final Configuration configuration,
+                                    final State state)
             throws XMLStreamException, ModelException {
-        final Element element = readElement(reader);
-        if (element.hasChildNodes()) {
-            final NodeList children = element.getChildNodes();
-            Node child = children.item(0);
-            final boolean cdata = child.getNodeType() == Node.CDATA_SECTION_NODE;
-            if (invokeContent) {
-                // search or and only use first <scxml> element
-                if (child.getNodeType() != Node.ELEMENT_NODE) {
-                    for (int i = 1, size = children.getLength(); i < size; i++) {
-                        child = children.item(i);
-                        if (child.getNodeType() == Node.ELEMENT_NODE) {
-                            break;
-                        }
-                    }
-                }
-                if (child.getNodeType() == Node.ELEMENT_NODE) {
-                    if (SCXMLConstants.ELEM_SCXML.equals(child.getLocalName()) &&
-                            SCXMLConstants.XMLNS_SCXML.equals(child.getNamespaceURI())) {
-                        // transform <invoke><content><scxml> back to text
-                        try {
-                            valueContainer.setParsedValue(new NodeTextValue(ContentParser.DEFAULT_PARSER.toXml(child)));
-                        } catch (final IOException e) {
-                            throw new XMLStreamException(e);
+
+        final Initial initial = new Initial();
+
+        loop : while (reader.hasNext()) {
+            String name, nsURI;
+            switch (reader.next()) {
+                case XMLStreamConstants.START_ELEMENT:
+                    nsURI = reader.getNamespaceURI();
+                    name = reader.getLocalName();
+                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
+                        if (SCXMLConstants.ELEM_TRANSITION.equals(name)) {
+                            initial.setTransition(readSimpleTransition(reader, configuration));
+                        } else {
+                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_INITIAL, nsURI, name);
                         }
+                    } else {
+                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_INITIAL, nsURI, name);
                     }
-                }
-                if (valueContainer.getParsedValue() == null) {
-                    reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_INVOKE, SCXMLConstants.XMLNS_SCXML,
-                            SCXMLConstants.ELEM_CONTENT);
-                }
-            }
-            else if (children.getLength() == 1 && (cdata || child.getNodeType() == Node.TEXT_NODE )) {
-                final String text = ContentParser.trimContent(child.getNodeValue());
-                if (ContentParser.hasJsonSignature(text)) {
-                    try {
-                        valueContainer.setParsedValue(new JsonValue(configuration.contentParser.parseJson(text), cdata));
-                    } catch (final IOException e) {
-                        throw new ModelException(e);
-                    }
-                }
-                else {
-                    valueContainer.setParsedValue(new TextValue(ContentParser.spaceNormalizeContent(text),
-                            cdata));
-                }
-            } else if (children.getLength() == 1) {
-                valueContainer.setParsedValue(new NodeValue(child));
-            } else {
-                final ArrayList<Node> nodeList = new ArrayList<>();
-                for (int i = 0, size = children.getLength(); i < size; i++) {
-                    nodeList.add(children.item(i));
-                }
-                valueContainer.setParsedValue(new NodeListValue(nodeList));
+                    break;
+                case XMLStreamConstants.END_ELEMENT:
+                    break loop;
+                default:
             }
         }
+
+        state.setInitial(initial);
     }
 
+    //---------------------- PRIVATE UTILITY METHODS ----------------------//
     /**
-     * Read the current element into a DOM {@link Element}.
+     * Parse the SCXML document at the supplied {@link URL} using the supplied {@link Configuration}, but do not
+     * wire up the object model to be usable just yet. Exactly one of the url, path, stream, reader or source
+     * parameters must be provided.
      *
-     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
+     * @param configuration The {@link Configuration} to use when parsing the SCXML document.
+     * @param scxmlURL The optional SCXML document {@link URL} to parse.
+     * @param scxmlPath The optional real path to the SCXML document as a string.
+     * @param scxmlStream The optional {@link InputStream} providing the SCXML document.
+     * @param scxmlReader The optional {@link Reader} providing the SCXML document.
+     * @param scxmlSource The optional {@link Source} providing the SCXML document.
      *
-     * @return The parsed content as a DOM {@link Element}.
+     * @return The parsed output, the Commons SCXML object model corresponding to the SCXML document
+     *         (not wired up to be immediately usable).
      *
+     * @throws IOException An IO error during parsing.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static Element readElement(final XMLStreamReader reader)
-            throws XMLStreamException {
+    private static SCXML readInternal(final Configuration configuration, final URL scxmlURL, final String scxmlPath,
+                                      final InputStream scxmlStream, final Reader scxmlReader, final Source scxmlSource)
+            throws IOException, ModelException, XMLStreamException {
 
-        // Create a document in which to build the DOM node
-        Document document;
-        try {
-            document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
-        } catch (final ParserConfigurationException pce) {
-            throw new XMLStreamException(ERR_PARSER_CFG);
+        if (configuration.pathResolver == null) {
+            if (scxmlURL != null) {
+                configuration.pathResolver = new URLResolver(scxmlURL);
+            } else if (scxmlPath != null) {
+                configuration.pathResolver = new URLResolver(new URL(scxmlPath));
+            }
         }
 
-        // This root element will be returned, add any attributes as specified
-        final Element root = document.createElementNS(reader.getNamespaceURI(), createQualifiedName(reader.getPrefix(), reader.getLocalName()));
-        document.appendChild(root);
-
-        boolean children = false, cdata = false;
-        Node parent = root;
+        final XMLStreamReader reader = getReader(configuration, scxmlURL, scxmlPath, scxmlStream, scxmlReader, scxmlSource);
 
-        // Convert stream to DOM node(s) while maintaining parent child relationships
-        loop : while (reader.hasNext()) {
-            Node child = null;
-            switch (reader.next()) {
-                case XMLStreamConstants.START_ELEMENT:
-                    if (!children && root.hasChildNodes()) {
-                        // remove any children
-                        root.setTextContent(null);
-                    }
-                    children = true;
-                    final Element elem = document.createElementNS(reader.getNamespaceURI(),
-                            createQualifiedName(reader.getPrefix(), reader.getLocalName()));
-                    for (int i = 0; i < reader.getAttributeCount(); i++) {
-                        final Attr attr = document.createAttributeNS(reader.getAttributeNamespace(i),
-                                createQualifiedName(reader.getAttributePrefix(i), reader.getAttributeLocalName(i)));
-                        attr.setValue(reader.getAttributeValue(i));
-                        elem.setAttributeNodeNS(attr);
-                    }
-                    parent.appendChild(elem);
-                    parent = elem;
-                    break;
-                case XMLStreamConstants.SPACE:
-                case XMLStreamConstants.CHARACTERS:
-                case XMLStreamConstants.ENTITY_REFERENCE:
-                    if (!children || parent != root) {
-                        child = document.createTextNode(reader.getText());
-                    }
-                    break;
-                case XMLStreamConstants.CDATA:
-                    if (!children || parent != root) {
-                        cdata = true;
-                        child = document.createCDATASection(reader.getText());
-                    }
-                    break;
-                case XMLStreamConstants.END_ELEMENT:
-                    parent = parent.getParentNode();
-                    if (parent == document) {
-                        break loop;
-                    }
-                    break;
-                default: // rest is ignored
-            }
-            if (child != null) {
-                parent.appendChild(child);
-            }
-        }
-        if (!children && root.hasChildNodes() && root.getChildNodes().getLength() > 1) {
-            final String text = root.getTextContent().trim();
-            if (!cdata) {
-                root.setTextContent(text);
-            } else {
-                root.setTextContent(null);
-                root.appendChild(document.createCDATASection(text));
-            }
-        }
-        return root;
+        return readDocument(reader, configuration);
     }
 
     /**
-     * Read the following body contents into a String.
+     * Read the contents of this &lt;invoke&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     *
-     * @return The body content read into a String.
+     * @param configuration The {@link Configuration} to use while parsing.
+     * @param parent The parent {@link TransitionalState} for this invoke.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static String readBody(final XMLStreamReader reader)
-            throws XMLStreamException {
+    private static void readInvoke(final XMLStreamReader reader, final Configuration configuration,
+                                   final TransitionalState parent)
+            throws XMLStreamException, ModelException {
 
-        final StringBuilder body = new StringBuilder();
+        final Invoke invoke = new Invoke();
+        invoke.setId(readAV(reader, SCXMLConstants.ATTR_ID));
+        invoke.setIdlocation(readAV(reader, SCXMLConstants.ATTR_IDLOCATION));
+        invoke.setSrc(readAV(reader, SCXMLConstants.ATTR_SRC));
+        invoke.setSrcexpr(readAV(reader, SCXMLConstants.ATTR_SRCEXPR));
+        invoke.setType(readAV(reader, SCXMLConstants.ATTR_TYPE));
+        invoke.setAutoForward(readBooleanAV(reader, SCXMLConstants.ELEM_INVOKE, SCXMLConstants.ATTR_AUTOFORWARD));
+        invoke.setNamelist(readAV(reader, SCXMLConstants.ATTR_NAMELIST));
 
-        // Add all body content to StringBuilder
         loop : while (reader.hasNext()) {
+            String name, nsURI;
             switch (reader.next()) {
                 case XMLStreamConstants.START_ELEMENT:
-                    logger.warn("Ignoring XML content in <script> element, encountered element: "
-                            + createQualifiedName(reader.getPrefix(), reader.getLocalName()));
-                    skipToEndElement(reader);
-                    break;
-                case XMLStreamConstants.SPACE:
-                case XMLStreamConstants.CHARACTERS:
-                case XMLStreamConstants.ENTITY_REFERENCE:
-                case XMLStreamConstants.CDATA:
-                    body.append(reader.getText());
-                    break;
-                case XMLStreamConstants.COMMENT:
+                    nsURI = reader.getNamespaceURI();
+                    name = reader.getLocalName();
+                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
+                        if (SCXMLConstants.ELEM_PARAM.equals(name)) {
+                            readParam(reader, configuration, invoke);
+                        } else if (SCXMLConstants.ELEM_FINALIZE.equals(name)) {
+                            readFinalize(reader, configuration, parent, invoke);
+                        } else if (SCXMLConstants.ELEM_CONTENT.equals(name)) {
+                            readContent(reader, configuration, invoke);
+                        } else {
+                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_INVOKE, nsURI, name);
+                        }
+                    } else {
+                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_INVOKE, nsURI, name);
+                    }
                     break;
                 case XMLStreamConstants.END_ELEMENT:
                     break loop;
-                default: // rest is ignored
+                default:
             }
         }
-        return body.toString();
-    }
 
-    /**
-     * @param prefix prefix
-     * @param localName localName
-     * @return a qualified name from a prefix and localName
-     */
-    private static String createQualifiedName(final String prefix, final String localName) {
-        return (prefix != null && !prefix.isEmpty() ? prefix + ":" : "") + localName;
+        parent.addInvoke(invoke);
     }
 
     /**
-     * @param input input string to check if null or empty after trim
-     * @return null if input is null or empty after trim()
+     * Read the contents of this &lt;log&gt; element.
+     *
+     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
+     * @param executable The parent {@link Executable} for this action.
+     * @param parent The optional parent {@link ActionsContainer} if this action is a child of one.
+     *
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static String nullIfEmpty(final String input) {
-        return input == null || input.trim().length()==0 ? null : input.trim();
+    private static void readLog(final XMLStreamReader reader, final Executable executable, final ActionsContainer parent)
+            throws XMLStreamException {
+
+        final Log log = new Log();
+        log.setExpr(readAV(reader, SCXMLConstants.ATTR_EXPR));
+        log.setLabel(readAV(reader, SCXMLConstants.ATTR_LABEL));
+        log.setParent(executable);
+        if (parent != null) {
+            parent.addAction(log);
+        } else {
+            executable.addAction(log);
+        }
+        skipToEndElement(reader);
     }
 
     /**
-     * Gets the attribute value at the current reader location.
+     * Read the current active namespace declarations.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param attrLocalName The attribute name whose value is needed.
-     *
-     * @return The value of the attribute.
+     * @return the map of active namespace declarations, null if none defined
      */
-    private static String readAV(final XMLStreamReader reader, final String attrLocalName) {
-        return nullIfEmpty(reader.getAttributeValue(XMLNS_DEFAULT, attrLocalName));
+    private static Map<String, String> readNamespaces(final XMLStreamReader reader) {
+        Map<String, String> namespaces = null;
+        if (reader.getNamespaceCount() > 0) {
+            namespaces = new LinkedHashMap<>();
+            for (int i = 0; i < reader.getNamespaceCount(); i++) {
+                namespaces.put(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
+            }
+        }
+        return namespaces;
     }
 
     /**
-     * Gets the Boolean attribute value at the current reader location.
+     * Read the contents of this &lt;onentry&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param elementName The name of the element for which the attribute value is needed.
-     * @param attrLocalName The attribute name whose value is needed.
+     * @param configuration The {@link Configuration} to use while parsing.
+     * @param es The parent {@link EnterableState} for this onentry.
      *
-     * @return The Boolean value of the attribute.
-     * @throws ModelException When the attribute value is not empty but neither "true" or "false".
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static Boolean readBooleanAV(final XMLStreamReader reader, final String elementName,
-                                         final String attrLocalName)
-            throws ModelException {
-        final String value = nullIfEmpty(reader.getAttributeValue(XMLNS_DEFAULT, attrLocalName));
-        final Boolean result = "true".equals(value) ? Boolean.TRUE : "false".equals(value) ? Boolean.FALSE : null;
-        if (result == null && value != null) {
-            final MessageFormat msgFormat = new MessageFormat(ERR_ATTRIBUTE_NOT_BOOLEAN);
-            final String errMsg = msgFormat.format(new Object[] {value, attrLocalName, elementName, reader.getLocation()});
-            throw new ModelException(errMsg);
-        }
-        return result;
+    private static void readOnEntry(final XMLStreamReader reader, final Configuration configuration,
+                                    final EnterableState es)
+            throws XMLStreamException, ModelException {
+
+        final OnEntry onentry = new OnEntry();
+        onentry.setRaiseEvent(readBooleanAV(reader, SCXMLConstants.ELEM_ONENTRY, SCXMLConstants.ATTR_EVENT));
+        readExecutableContext(reader, configuration, onentry, null);
+        es.addOnEntry(onentry);
     }
 
     /**
-     * Gets a required attribute value at the current reader location,
+     * Read the contents of this &lt;onexit&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @param elementName The name of the element for which the attribute value is needed.
-     * @param attrLocalName The attribute name whose value is needed.
+     * @param configuration The {@link Configuration} to use while parsing.
+     * @param es The parent {@link EnterableState} for this onexit.
      *
-     * @return The value of the attribute.
-     * @throws ModelException When the required attribute is missing or empty.
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static String readRequiredAV(final XMLStreamReader reader, final String elementName, final String attrLocalName)
-            throws ModelException {
-        final String value = nullIfEmpty(reader.getAttributeValue(XMLNS_DEFAULT, attrLocalName));
-        if (value == null) {
-            reportMissingAttribute(reader.getLocation(), elementName, attrLocalName);
-        }
-        return value;
+    private static void readOnExit(final XMLStreamReader reader, final Configuration configuration,
+                                   final EnterableState es)
+            throws XMLStreamException, ModelException {
+
+        final OnExit onexit = new OnExit();
+        onexit.setRaiseEvent(readBooleanAV(reader, SCXMLConstants.ELEM_ONEXIT, SCXMLConstants.ATTR_EVENT));
+        readExecutableContext(reader, configuration, onexit, null);
+        es.addOnExit(onexit);
     }
 
     private static String readOrGeneratedTransitionTargetId(final XMLStreamReader reader, final SCXML scxml,
@@ -2335,606 +2051,890 @@ public final class SCXMLReader {
     }
 
     /**
-     * Read the current active namespace declarations.
+     * Read the contents of this &lt;parallel&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
-     * @return the map of active namespace declarations, null if none defined
+     * @param configuration The {@link Configuration} to use while parsing.
+     * @param scxml The root of the object model being parsed.
+     * @param parent The parent {@link TransitionalState} for this parallel (null for top level state).
+     *
+     * @throws IOException An IO error during parsing.
+     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
+     *                        errors in the SCXML document that may not be identified by the schema).
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static Map<String, String> readNamespaces(final XMLStreamReader reader) {
-        Map<String, String> namespaces = null;
-        if (reader.getNamespaceCount() > 0) {
-            namespaces = new LinkedHashMap<>();
-            for (int i = 0; i < reader.getNamespaceCount(); i++) {
-                namespaces.put(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
-            }
-        }
-        return namespaces;
-    }
+    private static void readParallel(final XMLStreamReader reader, final Configuration configuration, final SCXML scxml,
+                                     final TransitionalState parent)
+            throws IOException, ModelException, XMLStreamException {
 
-    /**
-     * Report a missing required attribute value at the current reader location,
-     *
-     * @param location The (approximate) {@link Location} where the attribute is expected.
-     * @param elementName The name of the element for which the attribute value is needed.
-     * @param attrLocalName The attribute name whose value is needed.
-     *
-     * @throws ModelException The required attribute is missing or empty.
-     */
-    private static void reportMissingAttribute(final Location location, final String elementName, final String attrLocalName)
-            throws ModelException {
-        final MessageFormat msgFormat = new MessageFormat(ERR_REQUIRED_ATTRIBUTE_MISSING);
-        final String errMsg = msgFormat.format(new Object[] {elementName, attrLocalName, location});
-        throw new ModelException(errMsg);
+        final Parallel parallel = new Parallel();
+        parallel.setId(readOrGeneratedTransitionTargetId(reader, scxml, SCXMLConstants.ELEM_PARALLEL));
+        final String src = readAV(reader, SCXMLConstants.ATTR_SRC);
+        if (src != null) {
+            String source = src;
+            final Configuration copy = new Configuration(configuration);
+            if (copy.parent == null) {
+                copy.parent = scxml;
+            }
+            if (configuration.pathResolver != null) {
+                source = configuration.pathResolver.resolvePath(src);
+                copy.pathResolver = configuration.pathResolver.getResolver(src);
+            }
+            readTransitionalStateSrc(copy, source, parallel);
+        }
+
+        if (parent == null) {
+            scxml.addChild(parallel);
+        } else if (parent instanceof State) {
+            ((State)parent).addChild(parallel);
+        }
+        else {
+            ((Parallel)parent).addChild(parallel);
+        }
+        scxml.addTarget(parallel);
+        if (configuration.parent != null) {
+            configuration.parent.addTarget(parallel);
+        }
+
+        loop : while (reader.hasNext()) {
+            String name, nsURI;
+            switch (reader.next()) {
+                case XMLStreamConstants.START_ELEMENT:
+                    nsURI = reader.getNamespaceURI();
+                    name = reader.getLocalName();
+                    if (SCXMLConstants.XMLNS_SCXML.equals(nsURI)) {
+                        if (SCXMLConstants.ELEM_TRANSITION.equals(name)) {
+                            parallel.addTransition(readTransition(reader, configuration));
+                        } else if (SCXMLConstants.ELEM_STATE.equals(name)) {
+                            readState(reader, configuration, scxml, parallel);
+                        } else if (SCXMLConstants.ELEM_PARALLEL.equals(name)) {
+                            readParallel(reader, configuration, scxml, parallel);
+                        } else if (SCXMLConstants.ELEM_ONENTRY.equals(name)) {
+                            readOnEntry(reader, configuration, parallel);
+                        } else if (SCXMLConstants.ELEM_ONEXIT.equals(name)) {
+                            readOnExit(reader, configuration, parallel);
+                        } else if (SCXMLConstants.ELEM_DATAMODEL.equals(name)) {
+                            readDatamodel(reader, configuration, null, parallel);
+                        } else if (SCXMLConstants.ELEM_INVOKE.equals(name)) {
+                            readInvoke(reader, configuration, parallel);
+                        } else if (SCXMLConstants.ELEM_HISTORY.equals(name)) {
+                            readHistory(reader, configuration, scxml, parallel);
+                        } else {
+                            reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_PARALLEL, nsURI, name);
+                        }
+                    } else {
+                        reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_PARALLEL, nsURI, name);
+                    }
+                    break;
+                case XMLStreamConstants.END_ELEMENT:
+                    break loop;
+                default:
+            }
+        }
     }
 
     /**
-     * Report an ignored element via the {@link XMLReporter} if available and the class
-     * {@link org.apache.commons.logging.Log}.
+     * Read the contents of this &lt;param&gt; element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
-     * @param parent The parent element local name in the SCXML namespace.
-     * @param nsURI The namespace URI of the ignored element.
-     * @param name The local name of the ignored element.
+     * @param parent The parent {@link org.apache.commons.scxml2.model.ParamsContainer} for this param.
      *
      * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
-     * @throws ModelException The Commons SCXML object model is incomplete or inconsistent (includes
-     *                        errors in the SCXML document that may not be identified by the schema).
      */
-    private static void reportIgnoredElement(final XMLStreamReader reader, final Configuration configuration,
-                                             final String parent, final String nsURI, final String name)
+    private static void readParam(final XMLStreamReader reader, final Configuration configuration,
+                                  final ParamsContainer parent)
             throws XMLStreamException, ModelException {
 
-        final StringBuilder sb = new StringBuilder();
-        sb.append("Ignoring unknown or invalid element <").append(name)
-                .append("> in namespace \"").append(nsURI)
-                .append("\" as child of <").append(parent)
-                .append("> at ").append(reader.getLocation());
-        if (!configuration.isSilent() && logger.isWarnEnabled()) {
-            logger.warn(sb.toString());
+        final Param param = new Param();
+        param.setName(readRequiredAV(reader, SCXMLConstants.ELEM_PARAM, SCXMLConstants.ATTR_NAME));
+        final String location = readAV(reader, SCXMLConstants.ATTR_LOCATION);
+        final String expr = readAV(reader, SCXMLConstants.ATTR_EXPR);
+        if (expr != null) {
+            if (location != null) {
+                reportConflictingAttribute(reader, configuration, SCXMLConstants.ELEM_PARAM, SCXMLConstants.ATTR_LOCATION, SCXMLConstants.ATTR_EXPR);
+            }
+            else {
+                param.setExpr(expr);
+            }
         }
-        if (configuration.isStrict()) {
-            throw new ModelException(sb.toString());
+        else if (location == null) {
+            // force error missing required location or expr: use location attr for this
+            param.setLocation(readRequiredAV(reader, SCXMLConstants.ELEM_PARAM, SCXMLConstants.ATTR_LOCATION));
         }
-        final XMLReporter reporter = configuration.reporter;
-        if (reporter != null) {
-            reporter.report(sb.toString(), "COMMONS_SCXML", null, reader.getLocation());
+        else {
+            param.setLocation(location);
         }
+        parent.getParams().add(param);
         skipToEndElement(reader);
     }
 
     /**
-     * Advances the XMLStreamReader until after the end of the current element: all children will be skipped as well
-     * @param reader the reader
-     * @throws XMLStreamException
+     * Read and parse the body of a {@link ParsedValueContainer} element.
+     *
+     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
+     * @param configuration The {@link Configuration} to use while parsing.
+     * @param valueContainer The {@link ParsedValueContainer} element which body to read
+     * @param invokeContent flag indicating if the valueContainer is a <invoke><content> element, which get special
+     *                      treatment
+     *
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamReader}.
      */
-    private static void skipToEndElement(final XMLStreamReader reader) throws XMLStreamException {
-        int elementsToSkip = 1;
-        while (elementsToSkip > 0 && reader.hasNext()) {
-            final int next = reader.next();
-            if (next == XMLStreamConstants.START_ELEMENT) {
-                elementsToSkip++;
+    private static void readParsedValue(final XMLStreamReader reader, final Configuration configuration,
+                                        final ParsedValueContainer valueContainer, final boolean invokeContent)
+            throws XMLStreamException, ModelException {
+        final Element element = readElement(reader);
+        if (element.hasChildNodes()) {
+            final NodeList children = element.getChildNodes();
+            Node child = children.item(0);
+            final boolean cdata = child.getNodeType() == Node.CDATA_SECTION_NODE;
+            if (invokeContent) {
+                // search or and only use first <scxml> element
+                if (child.getNodeType() != Node.ELEMENT_NODE) {
+                    for (int i = 1, size = children.getLength(); i < size; i++) {
+                        child = children.item(i);
+                        if (child.getNodeType() == Node.ELEMENT_NODE) {
+                            break;
+                        }
+                    }
+                }
+                if (child.getNodeType() == Node.ELEMENT_NODE) {
+                    if (SCXMLConstants.ELEM_SCXML.equals(child.getLocalName()) &&
+                            SCXMLConstants.XMLNS_SCXML.equals(child.getNamespaceURI())) {
+                        // transform <invoke><content><scxml> back to text
+                        try {
+                            valueContainer.setParsedValue(new NodeTextValue(ContentParser.DEFAULT_PARSER.toXml(child)));
+                        } catch (final IOException e) {
+                            throw new XMLStreamException(e);
+                        }
+                    }
+                }
+                if (valueContainer.getParsedValue() == null) {
+                    reportIgnoredElement(reader, configuration, SCXMLConstants.ELEM_INVOKE, SCXMLConstants.XMLNS_SCXML,
+                            SCXMLConstants.ELEM_CONTENT);
+                }
             }
-            else if (next == XMLStreamConstants.END_ELEMENT) {
-                elementsToSkip--;
+            else if (children.getLength() == 1 && (cdata || child.getNodeType() == Node.TEXT_NODE )) {
+                final String text = ContentParser.trimContent(child.getNodeValue());
+                if (ContentParser.hasJsonSignature(text)) {
+                    try {
+                        valueContainer.setParsedValue(new JsonValue(configuration.contentParser.parseJson(text), cdata));
+                    } catch (final IOException e) {
+                        throw new ModelException(e);
+                    }
+                }
+                else {
+                    valueContainer.setParsedValue(new TextValue(ContentParser.spaceNormalizeContent(text),
+                            cdata));
+                }
+            } else if (children.getLength() == 1) {
... 8948 lines suppressed ...