Modified: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/javascript/JSEvaluator.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/javascript/JSEvaluator.java?rev=1639829&r1=1639828&r2=1639829&view=diff
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/javascript/JSEvaluator.java
 (original)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/javascript/JSEvaluator.java
 Sat Nov 15 03:07:58 2014
@@ -17,6 +17,7 @@
 
 package org.apache.commons.scxml2.env.javascript;
 
+import java.util.UUID;
 import java.util.regex.Pattern;
 
 import javax.script.Bindings;
@@ -24,14 +25,12 @@ import javax.script.ScriptContext;
 import javax.script.ScriptEngine;
 import javax.script.ScriptEngineManager;
 
-import org.apache.commons.scxml2.Builtin;
 import org.apache.commons.scxml2.Context;
 import org.apache.commons.scxml2.Evaluator;
 import org.apache.commons.scxml2.EvaluatorProvider;
 import org.apache.commons.scxml2.SCXMLExpressionException;
-import org.apache.commons.scxml2.SCXMLSystemContext;
+import org.apache.commons.scxml2.XPathBuiltin;
 import org.apache.commons.scxml2.model.SCXML;
-import org.w3c.dom.Node;
 
 /**
  * Embedded JavaScript expression evaluator for SCXML expressions. This
@@ -47,15 +46,18 @@ import org.w3c.dom.Node;
 
 public class JSEvaluator implements Evaluator {
 
-    // CONSTANTS
+    /**
+     * Unique context variable name used for temporary reference to assign 
data (thus must be a valid variable name)
+     */
+    private static final String ASSIGN_VARIABLE_NAME = 
"a"+UUID.randomUUID().toString().replace('-','x');
 
-    private static final String SUPPORTED_DATAMODEL = "ecmascript";
+    public static final String SUPPORTED_DATA_MODEL = 
Evaluator.ECMASCRIPT_DATA_MODEL;
 
     public static class JSEvaluatorProvider implements EvaluatorProvider {
 
         @Override
         public String getSupportedDatamodel() {
-            return SUPPORTED_DATAMODEL;
+            return SUPPORTED_DATA_MODEL;
         }
 
         @Override
@@ -73,6 +75,8 @@ public class JSEvaluator implements Eval
     private static final Pattern IN_FN = Pattern.compile("In\\(");
     /** Pattern for recognizing the Commons SCXML Data() builtin function. */
     private static final Pattern DATA_FN = Pattern.compile("Data\\(");
+    /** Pattern for recognizing the Commons SCXML Location() builtin function. 
*/
+    private static final Pattern LOCATION_FN = Pattern.compile("Location\\(");
 
     // INSTANCE VARIABLES
 
@@ -85,17 +89,15 @@ public class JSEvaluator implements Eval
      */
     public JSEvaluator() {
         factory = new ScriptEngineManager();
-        factory.put("_builtin", new Builtin());
     }
 
     // INSTANCE METHODS
 
     @Override
     public String getSupportedDatamodel() {
-        return SUPPORTED_DATAMODEL;
+        return SUPPORTED_DATA_MODEL;
     }
 
-
     /**
      * Creates a child context.
      *
@@ -130,11 +132,14 @@ public class JSEvaluator implements Eval
             Bindings     bindings = engine.getBindings     
(ScriptContext.ENGINE_SCOPE);
 
             // ... replace built-in functions
-            String jsExpression = 
IN_FN.matcher(expression).replaceAll("_builtin.isMember("+SCXMLSystemContext.ALL_STATES_KEY
 +", ");
-            jsExpression = 
DATA_FN.matcher(jsExpression).replaceAll("_builtin.data("+Context.NAMESPACES_KEY+",
 ");
+            String jsExpression = 
IN_FN.matcher(expression).replaceAll("_builtin.In(");
+            jsExpression = 
DATA_FN.matcher(jsExpression).replaceAll("_builtin.Data(");
+            jsExpression = 
LOCATION_FN.matcher(jsExpression).replaceAll("_builtin.Location(");
 
             // ... evaluate
-            return engine.eval(jsExpression,new JSBindings(context,bindings));
+            JSBindings jsBindings = new JSBindings(context, bindings);
+            jsBindings.put("_builtin", new JSFunctions(context));
+            return engine.eval(jsExpression,jsBindings);
 
         } catch (Exception x) {
             throw new SCXMLExpressionException("Error evaluating ['" + 
expression + "'] " + x);
@@ -155,14 +160,14 @@ public class JSEvaluator implements Eval
      */
     @Override
     public Boolean evalCond(Context context,String expression) throws 
SCXMLExpressionException {
-        Object object;
+        final Object result = eval(context,expression);
 
-        if ((object = eval(context,expression)) == null) {
+        if (result == null) {
            return Boolean.FALSE;
         }
 
-        if (object instanceof Boolean) {
-           return (Boolean) object;
+        if (result instanceof Boolean) {
+           return (Boolean)result;
         }
 
         throw new SCXMLExpressionException("Invalid boolean expression: " + 
expression);
@@ -181,23 +186,41 @@ public class JSEvaluator implements Eval
      * @throws SCXMLExpressionException Thrown if the expression was invalid.
      */
     @Override
-    public Node evalLocation(Context context,String expression) throws 
SCXMLExpressionException {
-        try {
-
-            // ... initialize
-            ScriptEngine engine   = factory.getEngineByName("JavaScript");
-            Bindings     bindings = engine.getBindings     
(ScriptContext.ENGINE_SCOPE);
+    public Object evalLocation(Context context,String expression) throws 
SCXMLExpressionException {
+        if (expression == null) {
+            return null;
+        }
+        else if (context.has(expression)) {
+            return expression;
+        }
+        return eval(context, expression);
+    }
 
-            // ... replace built-in functions
-            String jsExpression = 
IN_FN.matcher(expression).replaceAll("_builtin.isMember(_ALL_STATES, ");
-            jsExpression = 
DATA_FN.matcher(jsExpression).replaceFirst("_builtin.dataNode("+Context.NAMESPACES_KEY+",
 ");
-            jsExpression = 
DATA_FN.matcher(jsExpression).replaceAll("_builtin.data("+Context.NAMESPACES_KEY+",
 ");
+    /**
+     * @see Evaluator#evalAssign(Context, String, Object, AssignType, String)
+     */
+    public void evalAssign(final Context ctx, final String location, final 
Object data, final AssignType type,
+                           final String attr) throws SCXMLExpressionException {
 
-            // ... evaluate
-            return (Node) engine.eval(jsExpression,new 
JSBindings(context,bindings));
+        Object loc = evalLocation(ctx, location);
+        if (loc != null) {
 
-        } catch (Exception x) {
-            throw new SCXMLExpressionException("Error evaluating ['" + 
expression + "'] " + x);
+            if (XPathBuiltin.isXPathLocation(ctx, loc)) {
+                XPathBuiltin.assign(ctx, loc, data, type, attr);
+            }
+            else {
+                StringBuilder sb = new 
StringBuilder(location).append("=").append(ASSIGN_VARIABLE_NAME);
+                try {
+                    ctx.getVars().put(ASSIGN_VARIABLE_NAME, data);
+                    eval(ctx, sb.toString());
+                }
+                finally {
+                    ctx.getVars().remove(ASSIGN_VARIABLE_NAME);
+                }
+            }
+        }
+        else {
+            throw new SCXMLExpressionException("evalAssign - cannot resolve 
location: '" + location + "'");
         }
     }
 
@@ -220,6 +243,4 @@ public class JSEvaluator implements Eval
     throws SCXMLExpressionException {
         return eval(ctx, script);
     }
-
 }
-

Added: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/javascript/JSFunctions.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/javascript/JSFunctions.java?rev=1639829&view=auto
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/javascript/JSFunctions.java
 (added)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/javascript/JSFunctions.java
 Sat Nov 15 03:07:58 2014
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.scxml2.env.javascript;
+
+import org.apache.commons.scxml2.Builtin;
+import org.apache.commons.scxml2.Context;
+import org.apache.commons.scxml2.SCXMLExpressionException;
+import org.apache.commons.scxml2.XPathBuiltin;
+
+/**
+ * Custom Javascript engine function providing the SCXML In() predicate and 
the Commons SCXML extensions
+ * for Data() and Location() to support XPath datamodel access.
+ */
+public class JSFunctions {
+
+    /**
+     * The context currently in use for evaluation.
+     */
+    private Context ctx;
+
+    /**
+     * Creates a new instance, wraps the context.
+     * @param ctx the context in use
+     */
+    public JSFunctions(Context ctx) {
+        this.ctx = ctx;
+    }
+
+    /**
+     * Provides the SCXML standard In() predicate for SCXML documents.
+     * @param state The State ID to compare with
+     * @return true if this state is currently active
+     */
+    public boolean In(final String state) {
+        return Builtin.isMember(ctx, state);
+    }
+
+    /**
+     * Provides the Commons SCXML Data() predicate extension for SCXML 
documents.
+     * @param expression the XPath expression
+     * @return the data matching the expression
+     */
+    public Object Data(String expression) throws SCXMLExpressionException {
+        return XPathBuiltin.eval(ctx, expression);
+    }
+
+    /**
+     * Provides the Commons SCXML Location() predicate extension for SCXML 
documents.
+     * @param expression the XPath expression
+     * @return the location matching the expression
+     */
+    public Object Location(String expression) throws SCXMLExpressionException {
+        return XPathBuiltin.evalLocation(ctx, expression);
+    }
+}

Propchange: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/javascript/JSFunctions.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/javascript/JSFunctions.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlBuiltin.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlBuiltin.java?rev=1639829&r1=1639828&r2=1639829&view=diff
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlBuiltin.java
 (original)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlBuiltin.java
 Sat Nov 15 03:07:58 2014
@@ -16,17 +16,13 @@
  */
 package org.apache.commons.scxml2.env.jexl;
 
-import java.util.Map;
-import java.util.Set;
-
 import org.apache.commons.scxml2.Builtin;
-import org.apache.commons.scxml2.Context;
-import org.apache.commons.scxml2.SCXMLSystemContext;
-import org.apache.commons.scxml2.model.EnterableState;
+import org.apache.commons.scxml2.SCXMLExpressionException;
+import org.apache.commons.scxml2.XPathBuiltin;
 
 /**
- * Global JEXL namespace functor, implements Data() and In() operators.
- * Cooperates with JexlContext.
+ * Global JEXL namespace functor, providing the standard SCXML In() operator 
and the Commons SCXML extensions
+ * for Data() and Location() to support XPath datamodel access.
  */
 public final class JexlBuiltin {
     /**
@@ -43,45 +39,29 @@ public final class JexlBuiltin {
     }
 
     /**
-     * Gets the ALL_NAMESPACES map from context.
-     * @return the ALL_NAMESPACES map
-     */
-    @SuppressWarnings("unchecked")
-    private Map<String, String> getNamespaces() {
-        return (Map<String, String>) context.get(Context.NAMESPACES_KEY);
-    }
-
-    /**
-     * Gets the ALL_STATES set from context.
-     * @return the ALL_STATES set
+     * Provides the SCXML standard In() predicate for SCXML documents.
+     * @param state The State ID to compare with
+     * @return true if this state is currently active
      */
-    @SuppressWarnings("unchecked")
-    private Set<EnterableState> getAllStates() {
-        return (Set<EnterableState>) 
context.get(SCXMLSystemContext.ALL_STATES_KEY);
+    public boolean In(final String state) {
+        return Builtin.isMember(context, state);
     }
 
     /**
-     * Implements the Data() predicate for SCXML documents ( see Builtin#data 
).
-     * @param data the context node
-     * @param path the XPath expression
-     * @return the first node matching the path
+     * Provides the Commons SCXML Data() predicate extension for SCXML 
documents.
+     * @param expression the XPath expression
+     * @return the data matching the expression
      */
-    public Object Data(final Object data, final String path) {
-        // first call maps delegates to dataNode(), subsequent ones to data()
-        if (context.isEvaluatingLocation()) {
-            context.setEvaluatingLocation(false);
-            return Builtin.dataNode(getNamespaces(), data, path);
-        } else {
-            return Builtin.data(getNamespaces(), data, path);
-        }
+    public Object Data(final String expression) throws 
SCXMLExpressionException {
+        return XPathBuiltin.eval(context, expression);
     }
 
     /**
-     * Implements the In() predicate for SCXML documents ( see 
Builtin#isMember )
-     * @param state The State ID to compare with
-     * @return Whether this State belongs to this Set
+     * Provides the Commons SCXML Location() predicate extension for SCXML 
documents.
+     * @param expression the XPath expression
+     * @return the location matching the expression
      */
-    public boolean In(final String state) {
-        return Builtin.isMember(getAllStates(), state);
+    public Object Location(final String expression) throws 
SCXMLExpressionException {
+        return XPathBuiltin.evalLocation(context, expression);
     }
 }

Modified: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlContext.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlContext.java?rev=1639829&r1=1639828&r2=1639829&view=diff
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlContext.java
 (original)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlContext.java
 Sat Nov 15 03:07:58 2014
@@ -32,12 +32,6 @@ public class JexlContext extends SimpleC
     private static final long serialVersionUID = 1L;
 
     /**
-     * Internal flag to indicate whether it is to evaluate a location
-     * that returns a Node within an XML data tree.
-     */
-    private boolean evaluatingLocation = false;
-
-    /**
      * Constructor.
      */
     public JexlContext() {
@@ -46,11 +40,11 @@ public class JexlContext extends SimpleC
 
     /**
      * Constructor with initial vars.
-     *
+     * @param parent The parent context
      * @param initialVars The initial set of variables.
      */
-    public JexlContext(final Map<String, Object> initialVars) {
-        super(initialVars);
+    public JexlContext(final Context parent, final Map<String, Object> 
initialVars) {
+        super(parent, initialVars);
     }
 
     /**
@@ -61,22 +55,5 @@ public class JexlContext extends SimpleC
     public JexlContext(final Context parent) {
         super(parent);
     }
-
-    /**
-     * Returns the internal flag to indicate whether it is to evaluate a 
location
-     * that returns a Node within an XML data tree.
-     */
-    public boolean isEvaluatingLocation() {
-        return evaluatingLocation;
-    }
-
-    /**
-     * Sets the internal flag to indicate whether it is to evaluate a location
-     * that returns a Node within an XML data tree.
-     */
-    public void setEvaluatingLocation(boolean evaluatingLocation) {
-        this.evaluatingLocation = evaluatingLocation;
-    }
-
 }
 

Modified: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java?rev=1639829&r1=1639828&r2=1639829&view=diff
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java
 (original)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/jexl/JexlEvaluator.java
 Sat Nov 15 03:07:58 2014
@@ -19,6 +19,7 @@ package org.apache.commons.scxml2.env.je
 import java.io.Serializable;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.UUID;
 
 import org.apache.commons.jexl2.Expression;
 import org.apache.commons.jexl2.JexlEngine;
@@ -27,9 +28,9 @@ import org.apache.commons.scxml2.Context
 import org.apache.commons.scxml2.Evaluator;
 import org.apache.commons.scxml2.EvaluatorProvider;
 import org.apache.commons.scxml2.SCXMLExpressionException;
+import org.apache.commons.scxml2.XPathBuiltin;
 import org.apache.commons.scxml2.env.EffectiveContextMap;
 import org.apache.commons.scxml2.model.SCXML;
-import org.w3c.dom.Node;
 
 /**
  * Evaluator implementation enabling use of JEXL expressions in
@@ -44,13 +45,18 @@ public class JexlEvaluator implements Ev
     /** Serial version UID. */
     private static final long serialVersionUID = 1L;
 
-    private static final String SUPPORTED_DATAMODEL = "jexl";
+    /**
+     * Unique context variable name used for temporary reference to assign 
data (thus must be a valid variable name)
+     */
+    private static final String ASSIGN_VARIABLE_NAME = 
"a"+UUID.randomUUID().toString().replace('-','x');
+
+    public static final String SUPPORTED_DATA_MODEL = "jexl";
 
     public static class JexlEvaluatorProvider implements EvaluatorProvider {
 
         @Override
         public String getSupportedDatamodel() {
-            return SUPPORTED_DATAMODEL;
+            return SUPPORTED_DATA_MODEL;
         }
 
         @Override
@@ -134,7 +140,7 @@ public class JexlEvaluator implements Ev
 
     @Override
     public String getSupportedDatamodel() {
-        return SUPPORTED_DATAMODEL;
+        return SUPPORTED_DATA_MODEL;
     }
 
     /**
@@ -151,16 +157,12 @@ public class JexlEvaluator implements Ev
         if (expr == null) {
             return null;
         }
-        JexlContext jexlCtx = null;
-        if (ctx instanceof JexlContext) {
-            jexlCtx = (JexlContext) ctx;
-        } else {
+        if (!(ctx instanceof JexlContext)) {
             throw new SCXMLExpressionException(ERR_CTX_TYPE);
         }
-        Expression exp = null;
         try {
-            final JexlContext effective = getEffectiveContext(jexlCtx);
-            exp = getJexlEngine().createExpression(expr);
+            final JexlContext effective = 
getEffectiveContext((JexlContext)ctx);
+            Expression exp = getJexlEngine().createExpression(expr);
             return exp.evaluate(effective);
         } catch (Exception e) {
             String exMessage = e.getMessage() != null ? e.getMessage() : 
e.getClass().getCanonicalName();
@@ -176,17 +178,14 @@ public class JexlEvaluator implements Ev
         if (expr == null) {
             return null;
         }
-        JexlContext jexlCtx = null;
-        if (ctx instanceof JexlContext) {
-            jexlCtx = (JexlContext) ctx;
-        } else {
+        if (!(ctx instanceof JexlContext)) {
             throw new SCXMLExpressionException(ERR_CTX_TYPE);
         }
-        Expression exp = null;
         try {
-            final JexlContext effective = getEffectiveContext(jexlCtx);
-            exp = getJexlEngine().createExpression(expr);
-            return (Boolean) exp.evaluate(effective);
+            final JexlContext effective = 
getEffectiveContext((JexlContext)ctx);
+            Expression exp = getJexlEngine().createExpression(expr);
+            final Object result = exp.evaluate(effective);
+            return result == null ? Boolean.FALSE : (Boolean)result;
         } catch (Exception e) {
             String exMessage = e.getMessage() != null ? e.getMessage() : 
e.getClass().getCanonicalName();
             throw new SCXMLExpressionException("evalCond('" + expr + "'): " + 
exMessage, e);
@@ -196,23 +195,22 @@ public class JexlEvaluator implements Ev
     /**
      * @see Evaluator#evalLocation(Context, String)
      */
-    public Node evalLocation(final Context ctx, final String expr)
+    public Object evalLocation(final Context ctx, final String expr)
     throws SCXMLExpressionException {
         if (expr == null) {
             return null;
         }
-        JexlContext jexlCtx = null;
-        if (ctx instanceof JexlContext) {
-            jexlCtx = (JexlContext) ctx;
-        } else {
+        else if (ctx.has(expr)) {
+            return expr;
+        }
+
+        if (!(ctx instanceof JexlContext)) {
             throw new SCXMLExpressionException(ERR_CTX_TYPE);
         }
-        Expression exp = null;
         try {
-            final JexlContext effective = getEffectiveContext(jexlCtx);
-            effective.setEvaluatingLocation(true);
-            exp = getJexlEngine().createExpression(expr);
-            return (Node) exp.evaluate(effective);
+            final JexlContext effective = 
getEffectiveContext((JexlContext)ctx);
+            Expression exp = getJexlEngine().createExpression(expr);
+            return exp.evaluate(effective);
         } catch (Exception e) {
             String exMessage = e.getMessage() != null ? e.getMessage() : 
e.getClass().getCanonicalName();
             throw new SCXMLExpressionException("evalLocation('" + expr + "'): 
" + exMessage, e);
@@ -220,6 +218,34 @@ public class JexlEvaluator implements Ev
     }
 
     /**
+     * @see Evaluator#evalAssign(Context, String, Object, AssignType, String)
+     */
+    public void evalAssign(final Context ctx, final String location, final 
Object data, final AssignType type,
+                           final String attr) throws SCXMLExpressionException {
+
+        Object loc = evalLocation(ctx, location);
+        if (loc != null) {
+
+            if (XPathBuiltin.isXPathLocation(ctx, loc)) {
+                XPathBuiltin.assign(ctx, loc, data, type, attr);
+            }
+            else {
+                StringBuilder sb = new 
StringBuilder(location).append("=").append(ASSIGN_VARIABLE_NAME);
+                try {
+                    ctx.getVars().put(ASSIGN_VARIABLE_NAME, data);
+                    eval(ctx, sb.toString());
+                }
+                finally {
+                    ctx.getVars().remove(ASSIGN_VARIABLE_NAME);
+                }
+            }
+        }
+        else {
+            throw new SCXMLExpressionException("evalAssign - cannot resolve 
location: '" + location + "'");
+        }
+    }
+
+    /**
      * @see Evaluator#evalScript(Context, String)
      */
     public Object evalScript(final Context ctx, final String script)
@@ -227,17 +253,12 @@ public class JexlEvaluator implements Ev
         if (script == null) {
             return null;
         }
-        JexlContext jexlCtx = null;
-        if (ctx instanceof JexlContext) {
-            jexlCtx = (JexlContext) ctx;
-        } else {
+        if (!(ctx instanceof JexlContext)) {
             throw new SCXMLExpressionException(ERR_CTX_TYPE);
         }
-        Script jexlScript = null;
         try {
-            final JexlContext effective = getEffectiveContext(jexlCtx);
-            effective.setEvaluatingLocation(true);
-            jexlScript = getJexlEngine().createScript(script);
+            final JexlContext effective = getEffectiveContext((JexlContext) 
ctx);
+            final Script jexlScript = getJexlEngine().createScript(script);
             return jexlScript.execute(effective);
         } catch (Exception e) {
             String exMessage = e.getMessage() != null ? e.getMessage() : 
e.getClass().getCanonicalName();
@@ -260,7 +281,7 @@ public class JexlEvaluator implements Ev
      * Create the internal JexlEngine member during the initialization.
      * This method can be overriden to specify more detailed options
      * into the JexlEngine.
-     * @return
+     * @return new JexlEngine instance
      */
     protected JexlEngine createJexlEngine() {
         JexlEngine engine = new JexlEngine();
@@ -279,7 +300,7 @@ public class JexlEvaluator implements Ev
      * <P>
      * <EM>NOTE: The internal JexlEngine instance can be null when this is 
deserialized.</EM>
      * </P>
-     * @return
+     * @return the current JexlEngine
      */
     private JexlEngine getJexlEngine() {
         JexlEngine engine = jexlEngine;
@@ -305,8 +326,8 @@ public class JexlEvaluator implements Ev
      * @return The effective JexlContext for the path leading up to
      *         document root.
      */
-    private JexlContext getEffectiveContext(final JexlContext nodeCtx) {
-        return new JexlContext(new EffectiveContextMap(nodeCtx));
+    protected JexlContext getEffectiveContext(final JexlContext nodeCtx) {
+        return new JexlContext(nodeCtx, new EffectiveContextMap(nodeCtx));
     }
 }
 

Modified: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/xpath/XPathEvaluator.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/xpath/XPathEvaluator.java?rev=1639829&r1=1639828&r2=1639829&view=diff
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/xpath/XPathEvaluator.java
 (original)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/xpath/XPathEvaluator.java
 Sat Nov 15 03:07:58 2014
@@ -17,6 +17,9 @@
 package org.apache.commons.scxml2.env.xpath;
 
 import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.jxpath.ClassFunctions;
@@ -25,13 +28,19 @@ import org.apache.commons.jxpath.Functio
 import org.apache.commons.jxpath.JXPathContext;
 import org.apache.commons.jxpath.JXPathException;
 import org.apache.commons.jxpath.PackageFunctions;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.ri.model.VariablePointer;
 import org.apache.commons.scxml2.Context;
 import org.apache.commons.scxml2.Evaluator;
 import org.apache.commons.scxml2.EvaluatorProvider;
 import org.apache.commons.scxml2.SCXMLExpressionException;
 import org.apache.commons.scxml2.env.EffectiveContextMap;
 import org.apache.commons.scxml2.model.SCXML;
+import org.w3c.dom.Attr;
+import org.w3c.dom.CharacterData;
+import org.w3c.dom.Element;
 import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 /**
  * <p>An {@link Evaluator} implementation for XPath environments.</p>
@@ -44,13 +53,19 @@ public class XPathEvaluator implements E
     /** Serial version UID. */
     private static final long serialVersionUID = -3578920670869493294L;
 
-    private static final String SUPPORTED_DATAMODEL = "xpath";
+    public static final String SUPPORTED_DATA_MODEL = 
Evaluator.XPATH_DATA_MODEL;
+
+    /**
+     * Internal 'marker' list used for collecting the NodePointer results of 
an {@link #evalLocation(Context, String)}
+     */
+    private static class NodePointerList extends ArrayList<NodePointer> {
+    }
 
     public static class XPathEvaluatorProvider implements EvaluatorProvider {
 
         @Override
         public String getSupportedDatamodel() {
-            return SUPPORTED_DATAMODEL;
+            return SUPPORTED_DATA_MODEL;
         }
 
         @Override
@@ -95,7 +110,7 @@ public class XPathEvaluator implements E
 
     @Override
     public String getSupportedDatamodel() {
-        return SUPPORTED_DATAMODEL;
+        return SUPPORTED_DATA_MODEL;
     }
 
     /**
@@ -104,9 +119,15 @@ public class XPathEvaluator implements E
     @Override
     public Object eval(final Context ctx, final String expr)
             throws SCXMLExpressionException {
-        JXPathContext context = getContext(ctx);
         try {
-            return context.getValue(expr, String.class);
+            List list = getContext(ctx).selectNodes(expr);
+            if (list.isEmpty()) {
+                return null;
+            }
+            else if (list.size() == 1) {
+                return list.get(0);
+            }
+            return list;
         } catch (JXPathException xee) {
             throw new SCXMLExpressionException(xee.getMessage(), xee);
         }
@@ -118,9 +139,8 @@ public class XPathEvaluator implements E
     @Override
     public Boolean evalCond(final Context ctx, final String expr)
             throws SCXMLExpressionException {
-        JXPathContext context = getContext(ctx);
         try {
-            return (Boolean)context.getValue(expr, Boolean.class);
+            return (Boolean)getContext(ctx).getValue(expr, Boolean.class);
         } catch (JXPathException xee) {
             throw new SCXMLExpressionException(xee.getMessage(), xee);
         }
@@ -130,17 +150,43 @@ public class XPathEvaluator implements E
      * @see Evaluator#evalLocation(Context, String)
      */
     @Override
-    public Node evalLocation(final Context ctx, final String expr)
-            throws SCXMLExpressionException {
+    public Object evalLocation(final Context ctx, final String expr) throws 
SCXMLExpressionException {
         JXPathContext context = getContext(ctx);
         try {
-            return (Node)context.selectSingleNode(expr);
+            Iterator iterator = context.iteratePointers(expr);
+            Object pointer;
+            NodePointerList pointerList = null;
+            while (iterator.hasNext()) {
+                pointer = iterator.next();
+                if (pointer != null && pointer instanceof NodePointer && 
((NodePointer)pointer).getNode() != null) {
+                    if (pointerList == null) {
+                        pointerList = new NodePointerList();
+                    }
+                    pointerList.add((NodePointer)pointer);
+                }
+            }
+            return pointerList;
         } catch (JXPathException xee) {
             throw new SCXMLExpressionException(xee.getMessage(), xee);
         }
     }
 
     /**
+     * @see Evaluator#evalAssign(Context, String, Object, AssignType, String)
+     */
+    public void evalAssign(final Context ctx, final String location, final 
Object data, final AssignType type,
+                           final String attr) throws SCXMLExpressionException {
+
+        Object loc = evalLocation(ctx, location);
+        if (isXPathLocation(ctx, loc)) {
+            assign(ctx, loc, data, type, attr);
+        }
+        else {
+            throw new SCXMLExpressionException("evalAssign - cannot resolve 
location: '" + location + "'");
+        }
+    }
+
+    /**
      * @see Evaluator#evalScript(Context, String)
      */
     public Object evalScript(Context ctx, String script)
@@ -156,9 +202,169 @@ public class XPathEvaluator implements E
         return new XPathContext(parent);
     }
 
+    /**
+     * Determine if an {@link Evaluator#evalLocation(Context, String)} 
returned result represents an XPath location
+     * @param ctx variable context
+     * @param data result data from {@link Evaluator#evalLocation(Context, 
String)}
+     * @return true if the data represents an XPath location
+     */
+    @SuppressWarnings("unused")
+    public boolean isXPathLocation(final Context ctx, Object data) {
+        return data instanceof NodePointerList;
+    }
+
+    /**
+     * Assigns data to a location
+     *
+     * @param ctx variable context
+     * @param location location expression
+     * @param data the data to assign.
+     * @param type the type of assignment to perform, null assumes {@link 
Evaluator.AssignType#REPLACE_CHILDREN}
+     * @param attr the name of the attribute to add when using type {@link 
Evaluator.AssignType#ADD_ATTRIBUTE}
+     * @throws SCXMLExpressionException A malformed expression exception
+     * @see Evaluator#evalAssign(Context, String, Object, 
Evaluator.AssignType, String)
+     */
+    public void assign(final Context ctx, final Object location, final Object 
data, final AssignType type,
+                       final String attr) throws SCXMLExpressionException {
+        if (!isXPathLocation(ctx, location)) {
+            throw new SCXMLExpressionException("assign requires a 
NodePointerList as location but is of type: " +
+                    (location==null ? "(null)" : 
location.getClass().getName()));
+        }
+        for (NodePointer pointer : (NodePointerList)location) {
+            Object node = pointer.getNode();
+            if (node != null) {
+                if (node instanceof Node) {
+                    assign(ctx, (Node)node, pointer.asPath(), data, type != 
null ? type : AssignType.REPLACE_CHILDREN, attr);
+                }
+                else if (pointer instanceof VariablePointer) {
+                    if (type == AssignType.DELETE) {
+                        pointer.remove();
+                    }
+                    VariablePointer vp = (VariablePointer)pointer;
+                    Object variable = vp.getNode();
+                    if (variable instanceof Node) {
+                        assign(ctx, (Node)variable, pointer.asPath(), data, 
type != null ? type : AssignType.REPLACE_CHILDREN, attr);
+                    }
+                    else if (type == null || type == AssignType.REPLACE) {
+                        String variableName = vp.getName().getName();
+                        if (data instanceof CharacterData) {
+                            ctx.set(variableName, 
((CharacterData)data).getNodeValue());
+                        }
+                        else {
+                            ctx.set(variableName, data);
+                        }
+                    }
+                    else {
+                        throw new SCXMLExpressionException("Unsupported assign 
type +" +
+                                type.name()+" for XPath variable 
"+pointer.asPath());
+                    }
+                }
+                else {
+                    throw new SCXMLExpressionException("Unsupported XPath 
location pointer " +
+                            pointer.getClass().getName()+" for location 
"+pointer.asPath());
+                }
+            }
+            // else: silent ignore - NodePointerList should not have pointers 
without node
+        }
+    }
+
+    @SuppressWarnings("unused")
+    protected void assign(final Context ctx, final Node node, final String 
nodePath, final Object data,
+                          final AssignType type, final String attr) throws 
SCXMLExpressionException {
+
+        if (type == AssignType.DELETE) {
+            node.getParentNode().removeChild(node);
+        }
+        else if (node instanceof Element) {
+            Element element = (Element)node;
+            if (type == AssignType.ADD_ATTRIBUTE) {
+                if (attr == null) {
+                    throw new SCXMLExpressionException("Missing required 
attribute name for adding attribute at " +
+                            nodePath);
+                }
+                if (data == null) {
+                    throw new SCXMLExpressionException("Missing required data 
value for adding attribute " +
+                            attr + " to location " + nodePath);
+                }
+                element.setAttribute(attr, data.toString());
+            }
+            else {
+                Node dataNode = null;
+                if (type != AssignType.REPLACE_CHILDREN) {
+                    if (data == null) {
+                        throw new SCXMLExpressionException("Missing required 
data value for assign type "+type.name());
+                    }
+                    dataNode = data instanceof Node
+                            ? 
element.getOwnerDocument().importNode((Node)data, true)
+                            : 
element.getOwnerDocument().createTextNode(data.toString());
+                }
+                switch (type) {
+                    case REPLACE_CHILDREN:
+                        // quick way to delete all children
+                        element.setTextContent(null);
+                        if (data instanceof Node) {
+                            
element.appendChild(element.getOwnerDocument().importNode((Node)data, true));
+                        }
+                        else if (data instanceof List) {
+                            for (Object dataElement : (List)data) {
+                                if (dataElement instanceof Node) {
+                                    
element.appendChild(element.getOwnerDocument().importNode((Node)dataElement, 
true));
+                                }
+                                else if (dataElement != null) {
+                                    
element.appendChild(element.getOwnerDocument().createTextNode(dataElement.toString()));
+                                }
+                            }
+                        }
+                        else if (data instanceof NodeList) {
+                            NodeList list = (NodeList)data;
+                            for (int i = 0, size = list.getLength(); i < size; 
i++)
+                            
element.appendChild(element.getOwnerDocument().importNode(list.item(i), true));
+                        }
+                        else {
+                            
element.appendChild(element.getOwnerDocument().createTextNode(data.toString()));
+                        }
+                        // else if data == null: already taken care of above
+                        break;
+                    case FIRST_CHILD:
+                        element.insertBefore(dataNode, 
element.getFirstChild());
+                        break;
+                    case LAST_CHILD:
+                        element.appendChild(dataNode);
+                        break;
+                    case PREVIOUS_SIBLING:
+                        element.getParentNode().insertBefore(dataNode, 
element);
+                        break;
+                    case NEXT_SIBLING:
+                        element.getParentNode().insertBefore(dataNode, 
element.getNextSibling());
+                        break;
+                    case REPLACE:
+                        element.getParentNode().replaceChild(dataNode, 
element);
+                        break;
+                }
+            }
+        }
+        else if (node instanceof CharacterData) {
+            if (type != AssignType.REPLACE) {
+                throw new SCXMLExpressionException("Assign type "+ type.name() 
+
+                        " not supported for character data node at " + 
nodePath);
+            }
+            ((CharacterData)node).setData(data.toString());
+        }
+        else if (node instanceof Attr) {
+            if (type != AssignType.REPLACE) {
+                throw new SCXMLExpressionException("Assign type "+ type.name() 
+
+                        " not supported for node attribute at " + nodePath);
+            }
+            ((Attr)node).setValue(data.toString());
+        }
+        else {
+            throw new SCXMLExpressionException("Unsupported assign location 
Node type "+node.getNodeType());
+        }
+    }
+
 
     @SuppressWarnings("unchecked")
-    private JXPathContext getContext(final Context ctx) throws 
SCXMLExpressionException {
+    protected JXPathContext getContext(final Context ctx) throws 
SCXMLExpressionException {
         JXPathContext context = JXPathContext.newContext(jxpathContext, new 
EffectiveContextMap(ctx));
         context.setVariables(new ContextVariables(ctx));
         Map<String, String> namespaces = (Map<String, String>) 
ctx.get(Context.NAMESPACES_KEY);

Modified: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/xpath/XPathFunctions.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/xpath/XPathFunctions.java?rev=1639829&r1=1639828&r2=1639829&view=diff
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/xpath/XPathFunctions.java
 (original)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/env/xpath/XPathFunctions.java
 Sat Nov 15 03:07:58 2014
@@ -25,14 +25,19 @@ import org.apache.commons.scxml2.SCXMLSy
 import org.apache.commons.scxml2.model.EnterableState;
 
 /**
- * JXPath custom extension functions providing the SCXML In() function
+ * Commons JXPath custom extension function providing the SCXML In() predicate
  */
 public class XPathFunctions {
 
+    /**
+     * Provides the SCXML standard In() predicate for SCXML documents.
+     * @param expressionContext The context currently in use for evaluation
+     * @param state The State ID to compare with
+     * @return true if this state is currently active
+     */
     @SuppressWarnings("unchecked")
     public static boolean In(ExpressionContext expressionContext, String 
state) {
         Variables variables = 
expressionContext.getJXPathContext().getVariables();
-        Set<EnterableState> allStates = (Set<EnterableState>) 
variables.getVariable(SCXMLSystemContext.ALL_STATES_KEY);
-        return Builtin.isMember(allStates, state);
+        return Builtin.isMember((Set<EnterableState>) 
variables.getVariable(SCXMLSystemContext.ALL_STATES_KEY), state);
     }
 }

Modified: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/invoke/Invoker.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/invoke/Invoker.java?rev=1639829&r1=1639828&r2=1639829&view=diff
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/invoke/Invoker.java
 (original)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/invoke/Invoker.java
 Sat Nov 15 03:07:58 2014
@@ -68,6 +68,11 @@ import org.apache.commons.scxml2.Trigger
 public interface Invoker {
 
     /**
+     * @return get the invoke ID provided by the parent state machine executor
+     */
+    String getInvokeId();
+
+    /**
      * Set 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

Modified: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/invoke/SimpleSCXMLInvoker.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/invoke/SimpleSCXMLInvoker.java?rev=1639829&r1=1639828&r2=1639829&view=diff
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/invoke/SimpleSCXMLInvoker.java
 (original)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/invoke/SimpleSCXMLInvoker.java
 Sat Nov 15 03:07:58 2014
@@ -54,6 +54,14 @@ public class SimpleSCXMLInvoker implemen
     /** Cancellation status. */
     private boolean cancelled;
 
+
+    /**
+     * {@inheritDoc}.
+     */
+    public String getInvokeId() {
+        return parentStateId;
+    }
+
     /**
      * {@inheritDoc}.
      */

Modified: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/ModelUpdater.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/ModelUpdater.java?rev=1639829&r1=1639828&r2=1639829&view=diff
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/ModelUpdater.java
 (original)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/ModelUpdater.java
 Sat Nov 15 03:07:58 2014
@@ -109,20 +109,6 @@ final class ModelUpdater {
                     + " belonging to {1}";
 
     /**
-     * Error message when an &lt;invoke&gt; does not specify a "type"
-     * attribute.
-     */
-    private static final String ERR_INVOKE_NO_TYPE = "{0} contains "
-            + "<invoke> with no \"type\" attribute specified.";
-
-    /**
-     * Error message when an &lt;invoke&gt; does not specify a "src"
-     * or a "srcexpr" attribute.
-     */
-    private static final String ERR_INVOKE_NO_SRC = "{0} contains "
-            + "<invoke> without a \"src\" or \"srcexpr\" attribute specified.";
-
-    /**
      * Error message when an &lt;invoke&gt; specifies both "src" and "srcexpr"
      * attributes.
      */
@@ -290,12 +276,6 @@ final class ModelUpdater {
         }
 
         for (Invoke inv : state.getInvokes()) {
-            if (inv.getType() == null) {
-                logAndThrowModelError(ERR_INVOKE_NO_TYPE, new Object[] 
{getName(state)});
-            }
-            if (inv.getSrc() == null && inv.getSrcexpr() == null) {
-                logAndThrowModelError(ERR_INVOKE_NO_SRC, new Object[] 
{getName(state)});
-            }
             if (inv.getSrc() != null && inv.getSrcexpr() != null) {
                 logAndThrowModelError(ERR_INVOKE_AMBIGUOUS_SRC, new Object[] 
{getName(state)});
             }

Modified: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java?rev=1639829&r1=1639828&r2=1639829&view=diff
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java
 (original)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java
 Sat Nov 15 03:07:58 2014
@@ -47,6 +47,7 @@ import javax.xml.validation.SchemaFactor
 import javax.xml.validation.Validator;
 
 import org.apache.commons.logging.LogFactory;
+import org.apache.commons.scxml2.Evaluator;
 import org.apache.commons.scxml2.PathResolver;
 import org.apache.commons.scxml2.env.SimpleErrorHandler;
 import org.apache.commons.scxml2.env.URLResolver;
@@ -54,12 +55,15 @@ import org.apache.commons.scxml2.model.A
 import org.apache.commons.scxml2.model.ActionsContainer;
 import org.apache.commons.scxml2.model.Assign;
 import org.apache.commons.scxml2.model.Cancel;
+import org.apache.commons.scxml2.model.Content;
+import org.apache.commons.scxml2.model.ContentContainer;
 import org.apache.commons.scxml2.model.CustomAction;
 import org.apache.commons.scxml2.model.Data;
 import org.apache.commons.scxml2.model.Datamodel;
 import org.apache.commons.scxml2.model.Else;
 import org.apache.commons.scxml2.model.ElseIf;
 import org.apache.commons.scxml2.model.EnterableState;
+import org.apache.commons.scxml2.model.ParamsContainer;
 import org.apache.commons.scxml2.model.TransitionalState;
 import org.apache.commons.scxml2.model.Raise;
 import org.apache.commons.scxml2.model.Executable;
@@ -268,6 +272,7 @@ public final class SCXMLReader {
 
     //---- ATTRIBUTE NAMES ----//
     private static final String ATTR_ARRAY = "array";
+    private static final String ATTR_ATTR = "attr";
     private static final String ATTR_AUTOFORWARD = "autoforward";
     private static final String ATTR_COND = "cond";
     private static final String ATTR_DATAMODEL = "datamodel";
@@ -621,7 +626,7 @@ public final class SCXMLReader {
     private static void readSCXML(final XMLStreamReader reader, final 
Configuration configuration, final SCXML scxml)
             throws IOException, ModelException, XMLStreamException {
 
-        scxml.setDatamodelType(readAV(reader, ATTR_DATAMODEL));
+        scxml.setDatamodelName(readAV(reader, ATTR_DATAMODEL));
         scxml.setExmode(readAV(reader, ATTR_EXMODE));
         scxml.setInitial(readAV(reader, ATTR_INITIAL));
         scxml.setName(readAV(reader, ATTR_NAME));
@@ -662,12 +667,7 @@ public final class SCXMLReader {
                     break;
                 case XMLStreamConstants.END_ELEMENT:
                     popNamespaces(reader, configuration);
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (XMLNS_SCXML.equals(nsURI) && ELEM_SCXML.equals(name)) {
-                        break loop;
-                    }
-                    break;
+                    break loop;
                 default:
             }
         }
@@ -760,12 +760,7 @@ public final class SCXMLReader {
                     break;
                 case XMLStreamConstants.END_ELEMENT:
                     popNamespaces(reader, configuration);
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (XMLNS_SCXML.equals(nsURI) && ELEM_STATE.equals(name)) {
-                        break loop;
-                    }
-                    break;
+                    break loop;
                 default:
             }
         }
@@ -850,12 +845,7 @@ public final class SCXMLReader {
                     break;
                 case XMLStreamConstants.END_ELEMENT:
                     popNamespaces(reader, configuration);
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (XMLNS_SCXML.equals(nsURI) && 
ELEM_PARALLEL.equals(name)) {
-                        break loop;
-                    }
-                    break;
+                    break loop;
                 default:
             }
         }
@@ -913,12 +903,7 @@ public final class SCXMLReader {
                     break;
                 case XMLStreamConstants.END_ELEMENT:
                     popNamespaces(reader, configuration);
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (XMLNS_SCXML.equals(nsURI) && ELEM_FINAL.equals(name)) {
-                        break loop;
-                    }
-                    break;
+                    break loop;
                 default:
             }
         }
@@ -952,7 +937,7 @@ public final class SCXMLReader {
         }
 
         // Parse external document
-        SCXML externalSCXML = null;
+        SCXML externalSCXML;
         try {
             externalSCXML = SCXMLReader.readInternal(configuration, new 
URL(location), null, null, null, null);
         } catch (Exception e) {
@@ -1082,12 +1067,7 @@ public final class SCXMLReader {
                     break;
                 case XMLStreamConstants.END_ELEMENT:
                     popNamespaces(reader, configuration);
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (XMLNS_SCXML.equals(nsURI) && 
ELEM_DATAMODEL.equals(name)) {
-                        break loop;
-                    }
-                    break;
+                    break loop;
                 default:
             }
         }
@@ -1115,7 +1095,7 @@ public final class SCXMLReader {
         datum.setId(readRequiredAV(reader, ELEM_DATA, ATTR_ID));
         datum.setExpr(readAV(reader, ATTR_EXPR));
         readNamespaces(configuration, datum);
-        datum.setNode(readNode(reader, configuration, XMLNS_SCXML, ELEM_DATA, 
new String[] {"id"}));
+        datum.setNode(readNode(reader, configuration, XMLNS_SCXML, ELEM_DATA, 
new String[]{"id"}));
         dm.addData(datum);
     }
 
@@ -1156,7 +1136,7 @@ public final class SCXMLReader {
                         } else if (ELEM_FINALIZE.equals(name)) {
                             readFinalize(reader, configuration, parent, 
invoke);
                         } else if (ELEM_CONTENT.equals(name)) {
-                            readContent(reader, configuration, parent, invoke);
+                            readContent(reader, configuration, invoke);
                         } else {
                             reportIgnoredElement(reader, configuration, 
ELEM_INVOKE, nsURI, name);
                         }
@@ -1166,12 +1146,7 @@ public final class SCXMLReader {
                     break;
                 case XMLStreamConstants.END_ELEMENT:
                     popNamespaces(reader, configuration);
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (XMLNS_SCXML.equals(nsURI) && ELEM_INVOKE.equals(name)) 
{
-                        break loop;
-                    }
-                    break;
+                    break loop;
                 default:
             }
         }
@@ -1184,19 +1159,36 @@ public final class SCXMLReader {
      *
      * @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 Invoke} for this param.
+     * @param parent The parent {@link 
org.apache.commons.scxml2.model.ParamsContainer} for this param.
      *
      * @throws XMLStreamException An exception processing the underlying 
{@link XMLStreamReader}.
      */
     private static void readParam(final XMLStreamReader reader, final 
Configuration configuration,
-                                  final Invoke parent)
+                                  final ParamsContainer parent)
             throws XMLStreamException, ModelException {
 
         Param param = new Param();
         param.setName(readRequiredAV(reader, ELEM_PARAM, ATTR_NAME));
-        param.setExpr(readAV(reader, ATTR_EXPR));
+        String location = readAV(reader, ATTR_LOCATION);
+        String expr = readAV(reader, ATTR_EXPR);
+        if (expr != null) {
+            if (location != null) {
+                reportConflictingAttribute(reader, configuration, ELEM_PARAM, 
ATTR_LOCATION, 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, ELEM_PARAM, 
ATTR_LOCATION));
+        }
+        else {
+            param.setLocation(location);
+        }
         readNamespaces(configuration, param);
-        parent.addParam(param);
+        parent.getParams().add(param);
+        skipToEndElement(reader);
     }
 
     /**
@@ -1226,16 +1218,32 @@ public final class SCXMLReader {
      *
      * @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 content.
+     * @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 TransitionalState state, final 
Invoke invoke)
+                                    final ContentContainer contentContainer)
             throws XMLStreamException {
 
-        // TODO content support
+        Content content = new Content();
+        content.setExpr(readAV(reader, ATTR_EXPR));
+        if (content.getExpr() != null) {
+            skipToEndElement(reader);
+        }
+        else {
+            Node body = readNode(reader, configuration, XMLNS_SCXML, 
ELEM_CONTENT, new String[]{});
+            if (body.hasChildNodes()) {
+                NodeList children = body.getChildNodes();
+                if (children.getLength() == 1 && 
children.item(0).getNodeType() == Node.TEXT_NODE) {
+                    content.setBody(children.item(0).getNodeValue());
+                }
+                else {
+                    content.setBody(body);
+                }
+            }
+        }
+        contentContainer.setContent(content);
     }
 
     /**
@@ -1274,12 +1282,7 @@ public final class SCXMLReader {
                     break;
                 case XMLStreamConstants.END_ELEMENT:
                     popNamespaces(reader, configuration);
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (XMLNS_SCXML.equals(nsURI) && 
ELEM_INITIAL.equals(name)) {
-                        break loop;
-                    }
-                    break;
+                    break loop;
                 default:
             }
         }
@@ -1329,12 +1332,7 @@ public final class SCXMLReader {
                     break;
                 case XMLStreamConstants.END_ELEMENT:
                     popNamespaces(reader, configuration);
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (XMLNS_SCXML.equals(nsURI) && 
ELEM_HISTORY.equals(name)) {
-                        break loop;
-                    }
-                    break;
+                    break loop;
                 default:
             }
         }
@@ -1534,12 +1532,7 @@ public final class SCXMLReader {
                     break;
                 case XMLStreamConstants.END_ELEMENT:
                     popNamespaces(reader, configuration);
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (XMLNS_SCXML.equals(nsURI) && end.equals(name)) {
-                        break loop;
-                    }
-                    break;
+                    break loop;
                 default:
             }
         }
@@ -1567,15 +1560,17 @@ public final class SCXMLReader {
             // In particular, the <send> and <raise> elements MUST NOT occur.
             reportIgnoredElement(reader, configuration, ELEM_FINALIZE, 
XMLNS_SCXML, ELEM_RAISE);
         }
-
-        Raise raise = new Raise();
-        raise.setEvent(readAV(reader, ATTR_EVENT));
-        readNamespaces(configuration, raise);
-        raise.setParent(executable);
-        if (parent != null) {
-            parent.addAction(raise);
-        } else {
-            executable.addAction(raise);
+        else {
+            Raise raise = new Raise();
+            raise.setEvent(readAV(reader, ATTR_EVENT));
+            readNamespaces(configuration, raise);
+            raise.setParent(executable);
+            if (parent != null) {
+                parent.addAction(raise);
+            } else {
+                executable.addAction(raise);
+            }
+            skipToEndElement(reader);
         }
     }
 
@@ -1626,6 +1621,7 @@ public final class SCXMLReader {
         readNamespaces(configuration, elseif);
         elseif.setParent(executable);
         iff.addAction(elseif);
+        skipToEndElement(reader);
     }
 
     /**
@@ -1646,6 +1642,7 @@ public final class SCXMLReader {
         readNamespaces(configuration, els);
         els.setParent(executable);
         iff.addAction(els);
+        skipToEndElement(reader);
     }
 
     /**
@@ -1702,6 +1699,7 @@ public final class SCXMLReader {
         } else {
             executable.addAction(log);
         }
+        skipToEndElement(reader);
     }
 
     /**
@@ -1720,13 +1718,22 @@ public final class SCXMLReader {
 
         Assign assign = new Assign();
         assign.setExpr(readAV(reader, ATTR_EXPR));
-        assign.setName(readAV(reader, ATTR_NAME));
-        if (assign.getName() != null && assign.getName().trim().length() > 0) {
-            // if 'non-standard' name attribute is defined, don't require 
location (as per the spec. 20130831)
-            assign.setLocation(readAV(reader, ATTR_LOCATION));
+        assign.setLocation(readRequiredAV(reader, ELEM_ASSIGN, ATTR_LOCATION));
+        String attrValue = readAV(reader, ATTR_TYPE);
+        if (attrValue != null) {
+            assign.setType(Evaluator.AssignType.fromValue(attrValue));
+            if (assign.getType() == null) {
+                reportIgnoredAttribute(reader, configuration, ELEM_ASSIGN, 
ATTR_TYPE, attrValue);
+            }
         }
-        else {
-            assign.setLocation(readRequiredAV(reader, ELEM_ASSIGN, 
ATTR_LOCATION));
+        attrValue = readAV(reader, ATTR_ATTR);
+        if (attrValue != null) {
+            if (Evaluator.AssignType.ADD_ATTRIBUTE.equals(assign.getType())) {
+                assign.setAttr(attrValue);
+            }
+            else {
+                reportIgnoredAttribute(reader, configuration, ELEM_ASSIGN, 
ATTR_ATTR, attrValue);
+            }
         }
         assign.setSrc(readAV(reader, ATTR_SRC));
         assign.setPathResolver(configuration.pathResolver);
@@ -1737,6 +1744,7 @@ public final class SCXMLReader {
         } else {
             executable.addAction(assign);
         }
+        skipToEndElement(reader);
     }
 
     /**
@@ -1760,6 +1768,7 @@ public final class SCXMLReader {
             // [...] 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, ELEM_FINALIZE, 
XMLNS_SCXML, ELEM_SEND);
+            return;
         }
 
         Send send = new Send();
@@ -1817,11 +1826,40 @@ public final class SCXMLReader {
         }
         readNamespaces(configuration, send);
 
-        Node body = readNode(reader, configuration, XMLNS_SCXML, ELEM_SEND, 
new String [] {});
-        NodeList childNodes = body.getChildNodes();
-        List<Node> externalNodes = send.getExternalNodes();
-        for (int i = 0; i < childNodes.getLength(); i++) {
-            externalNodes.add(childNodes.item(i));
+        loop : while (reader.hasNext()) {
+            String name, nsURI;
+            switch (reader.next()) {
+                case XMLStreamConstants.START_ELEMENT:
+                    pushNamespaces(reader, configuration);
+                    nsURI = reader.getNamespaceURI();
+                    name = reader.getLocalName();
+                    if (XMLNS_SCXML.equals(nsURI)) {
+                        if (ELEM_PARAM.equals(name)) {
+                            if (send.getContent() == null) {
+                                readParam(reader, configuration, send);
+                            }
+                            else {
+                                reportIgnoredElement(reader, configuration, 
ELEM_SEND, nsURI, name);
+                            }
+                        } else if (ELEM_CONTENT.equals(name)) {
+                            if (send.getNamelist() == null && 
send.getParams().isEmpty()) {
+                                readContent(reader, configuration, send);
+                            }
+                            else {
+                                reportIgnoredElement(reader, configuration, 
ELEM_SEND, nsURI, name);
+                            }
+                        } else {
+                            reportIgnoredElement(reader, configuration, 
ELEM_SEND, nsURI, name);
+                        }
+                    } else {
+                        reportIgnoredElement(reader, configuration, ELEM_SEND, 
nsURI, name);
+                    }
+                    break;
+                case XMLStreamConstants.END_ELEMENT:
+                    popNamespaces(reader, configuration);
+                    break loop;
+                default:
+            }
         }
 
         send.setParent(executable);
@@ -1854,6 +1892,7 @@ public final class SCXMLReader {
         } else {
             executable.addAction(cancel);
         }
+        skipToEndElement(reader);
     }
 
     /**
@@ -1872,7 +1911,7 @@ public final class SCXMLReader {
 
         Script script = new Script();
         readNamespaces(configuration, script);
-        script.setBody(readBody(reader, configuration, XMLNS_SCXML, 
ELEM_SCRIPT));
+        script.setBody(readBody(reader));
         script.setParent(executable);
         if (parent != null) {
             parent.addAction(script);
@@ -1899,7 +1938,7 @@ public final class SCXMLReader {
         Script globalScript = new Script();
         globalScript.setGlobalScript(true);
         readNamespaces(configuration, globalScript);
-        globalScript.setBody(readBody(reader, configuration, XMLNS_SCXML, 
ELEM_SCRIPT));
+        globalScript.setBody(readBody(reader));
         scxml.setGlobalScript(globalScript);
     }
 
@@ -1927,6 +1966,7 @@ public final class SCXMLReader {
         } else {
             executable.addAction(var);
         }
+        skipToEndElement(reader);
     }
 
     /**
@@ -1946,7 +1986,7 @@ public final class SCXMLReader {
             throws XMLStreamException {
 
         // Instantiate custom action
-        Object actionObject = null;
+        Object actionObject;
         String className = customAction.getActionClass().getName();
         ClassLoader cl = configuration.customActionClassLoader;
         if (configuration.useContextClassLoaderForCustomActions) {
@@ -1955,7 +1995,7 @@ public final class SCXMLReader {
         if (cl == null) {
             cl = SCXMLReader.class.getClassLoader();
         }
-        Class<?> clazz = null;
+        Class<?> clazz;
         try {
             clazz = cl.loadClass(className);
             actionObject = clazz.newInstance();
@@ -1976,7 +2016,7 @@ public final class SCXMLReader {
             String name = reader.getAttributeLocalName(i);
             String value = reader.getAttributeValue(i);
             String setter = "set" + name.substring(0, 1).toUpperCase() + 
name.substring(1);
-            Method method = null;
+            Method method;
             try {
                 method = clazz.getMethod(setter, String.class);
                 method.invoke(action, value);
@@ -2002,6 +2042,9 @@ public final class SCXMLReader {
                 externalNodes.add(childNodes.item(i));
             }
         }
+        else {
+            skipToEndElement(reader);
+        }
 
         // Wire in the action and add to parent
         readNamespaces(configuration, action);
@@ -2018,10 +2061,8 @@ public final class SCXMLReader {
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document 
to parse.
      * @param configuration The {@link Configuration} to use while parsing.
-     * @param namespaceURI The namespace URI of the parent element (we will 
stop parsing into DOM nodes when we reach
-     *                     the corresponding end tag)
-     * @param localName The local name of the parent element (we will stop 
parsing into DOM nodes when we reach the
-     *                  corresponding end tag)
+     * @param namespaceURI The namespace URI of the parent element
+     * @param localName The local name of the parent element
      * @param attrs The attributes that will be read into the root DOM node.
      *
      * @return The parsed content as a DOM {@link Node}.
@@ -2033,7 +2074,7 @@ public final class SCXMLReader {
             throws XMLStreamException {
 
         // Create a document in which to build the DOM node
-        Document document = null;
+        Document document;
         try {
             document = 
DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
         } catch (ParserConfigurationException pce) {
@@ -2042,18 +2083,15 @@ public final class SCXMLReader {
 
         // This root element will be returned, add any attributes as specified
         Element root = document.createElementNS(namespaceURI, localName);
-        for (int i = 0; i < attrs.length; i++) {
-            Attr attr = document.createAttributeNS(XMLNS_DEFAULT, attrs[i]);
-            attr.setValue(readAV(reader, attrs[i]));
+        for (final String attr1 : attrs) {
+            Attr attr = document.createAttributeNS(XMLNS_DEFAULT, attr1);
+            attr.setValue(readAV(reader, attr1));
             root.setAttributeNodeNS(attr);
         }
         document.appendChild(root);
 
-        // <data> can have only one child element, flags to skip any text 
nodes on either side
-        boolean one = (XMLNS_SCXML.equals(namespaceURI) && 
ELEM_DATA.equals(localName));
-        boolean text = false;
+        boolean children = false;
         Node parent = root;
-        String nodeNSURI = null, nodeName = null;
 
         // Convert stream to DOM node(s) while maintaining parent child 
relationships
         loop : while (reader.hasNext()) {
@@ -2061,14 +2099,14 @@ public final class SCXMLReader {
             Node child = null;
             switch (reader.next()) {
                 case XMLStreamConstants.START_ELEMENT:
+                    if (!children && root.hasChildNodes()) {
+                        // remove any children
+                        root.setTextContent(null);
+                    }
+                    children = true;
                     pushNamespaces(reader, configuration);
                     nsURI = reader.getNamespaceURI();
                     name = reader.getLocalName();
-                    if (!text) {
-                        nodeNSURI = nsURI;
-                        nodeName = name;
-                        text = true;
-                    }
                     Element elem = document.createElementNS(nsURI, name);
                     for (int i = 0; i < reader.getAttributeCount(); i++) {
                         nsURI = reader.getAttributeNamespace(i);
@@ -2087,30 +2125,23 @@ public final class SCXMLReader {
                 case XMLStreamConstants.SPACE:
                 case XMLStreamConstants.CHARACTERS:
                 case XMLStreamConstants.ENTITY_REFERENCE:
-                    if (text || !one) {
+                    if (!children || parent != root) {
                         child = document.createTextNode(reader.getText());
                     }
                     break;
                 case XMLStreamConstants.CDATA:
+                    children = true;
                     child = document.createCDATASection(reader.getText());
                     break;
                 case XMLStreamConstants.COMMENT:
+                    children = true;
                     child = document.createComment(reader.getText());
                     break;
                 case XMLStreamConstants.END_ELEMENT:
                     popNamespaces(reader, configuration);
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (namespaceURI.equals(nsURI) && localName.equals(name)) {
-                        break loop;
-                    }
-                    if (((nodeNSURI == null && nsURI == null) || (nodeNSURI != 
null && nodeNSURI.equals(nsURI)))
-                            && name.equals(nodeName)) {
-                        text = false;
-                    }
                     parent = parent.getParentNode();
                     if (parent == document) {
-                        parent = root;
+                        break loop;
                     }
                     break;
                 default: // rest is ignored
@@ -2119,6 +2150,9 @@ public final class SCXMLReader {
                 parent.appendChild(child);
             }
         }
+        if (!children && root.hasChildNodes()) {
+            root.setTextContent(root.getTextContent().trim());
+        }
         return root;
     }
 
@@ -2126,31 +2160,25 @@ public final class SCXMLReader {
      * 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 namespaceURI The namespace URI of the parent element (we will 
stop reading content when we reach
-     *                     the corresponding end tag)
-     * @param localName The local name of the parent element (we will stop 
reading content when we reach the
-     *                  corresponding end tag)
      *
      * @return The body content read into a String.
      *
      * @throws XMLStreamException An exception processing the underlying 
{@link XMLStreamReader}.
      */
-    private static String readBody(final XMLStreamReader reader, final 
Configuration configuration,
-                                   final String namespaceURI, final String 
localName)
+    private static String readBody(final XMLStreamReader reader)
             throws XMLStreamException {
 
-        StringBuffer body = new StringBuffer();
+        StringBuilder body = new StringBuilder();
         org.apache.commons.logging.Log log;
 
-        // Add all body content to StringBuffer
+        // Add all body content to StringBuilder
         loop : while (reader.hasNext()) {
-            String name, nsURI;
             switch (reader.next()) {
                 case XMLStreamConstants.START_ELEMENT:
                     log = LogFactory.getLog(SCXMLReader.class);
-                    log.warn("Ignoring XML content in <script> element, 
encountered start tag with local name: "
+                    log.warn("Ignoring XML content in <script> element, 
encountered element with local name: "
                             + reader.getLocalName());
+                    skipToEndElement(reader);
                     break;
                 case XMLStreamConstants.SPACE:
                 case XMLStreamConstants.CHARACTERS:
@@ -2160,16 +2188,7 @@ public final class SCXMLReader {
                     body.append(reader.getText());
                     break;
                 case XMLStreamConstants.END_ELEMENT:
-                    nsURI = reader.getNamespaceURI();
-                    name = reader.getLocalName();
-                    if (namespaceURI.equals(nsURI) && localName.equals(name)) {
-                        popNamespaces(reader, configuration);
-                        break loop;
-                    }
-                    log = LogFactory.getLog(SCXMLReader.class);
-                    log.warn("Ignoring XML content in <script> element, 
encountered end tag with local name: "
-                            + reader.getLocalName());
-                    break;
+                    break loop;
                 default: // rest is ignored
             }
         }
@@ -2300,6 +2319,59 @@ public final class SCXMLReader {
         if (reporter != null) {
             reporter.report(sb.toString(), "COMMONS_SCXML", null, 
reader.getLocation());
         }
+        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
+     */
+    private static void skipToEndElement(final XMLStreamReader reader) throws 
XMLStreamException {
+        int elementsToSkip = 1;
+        while (elementsToSkip > 0 && reader.hasNext()) {
+            int next = reader.next();
+            if (next == XMLStreamConstants.START_ELEMENT) {
+                elementsToSkip++;
+            }
+            else if (next == XMLStreamConstants.END_ELEMENT) {
+                elementsToSkip--;
+            }
+        }
+    }
+
+    /**
+     * Report an ignored attribute via the {@link XMLReporter} if available 
and the class
+     * {@link org.apache.commons.logging.Log}.
+     *
+     * @param reader The {@link XMLStreamReader} providing the SCXML document 
to parse.
+     * @param configuration The {@link Configuration} to use while parsing.
+     * @param element The element name.
+     * @param attr The attribute which is ignored.
+     * @param value The value of the attribute which is ignored.
+     *
+     * @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 reportIgnoredAttribute(final XMLStreamReader reader, 
final Configuration configuration,
+                                               final String element, final 
String attr, final String value)
+            throws XMLStreamException, ModelException {
+
+        org.apache.commons.logging.Log log = 
LogFactory.getLog(SCXMLReader.class);
+        StringBuilder sb = new StringBuilder();
+        sb.append("Ignoring unknown or invalid <").append(element).append("> 
attribute ").append(attr)
+                .append("=\"").append(value).append("\" at 
").append(reader.getLocation());
+        if (!configuration.isSilent() && log.isWarnEnabled()) {
+            log.warn(sb.toString());
+        }
+        if (configuration.isStrict()) {
+            throw new ModelException(sb.toString());
+        }
+        XMLReporter reporter = configuration.reporter;
+        if (reporter != null) {
+            reporter.report(sb.toString(), "COMMONS_SCXML", null, 
reader.getLocation());
+        }
     }
 
     /**
@@ -2434,7 +2506,7 @@ public final class SCXMLReader {
 
             URL scxmlSchema = new URL("TODO"); // TODO, point to appropriate 
location
             SchemaFactory schemaFactory = 
SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema";);
-            Schema schema = null;
+            Schema schema;
             try {
                 schema = schemaFactory.newSchema(scxmlSchema);
             } catch (SAXException se) {

Modified: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/SCXMLWriter.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/SCXMLWriter.java?rev=1639829&r1=1639828&r2=1639829&view=diff
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/SCXMLWriter.java
 (original)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/SCXMLWriter.java
 Sat Nov 15 03:07:58 2014
@@ -45,6 +45,7 @@ import org.apache.commons.logging.LogFac
 import org.apache.commons.scxml2.model.Action;
 import org.apache.commons.scxml2.model.Assign;
 import org.apache.commons.scxml2.model.Cancel;
+import org.apache.commons.scxml2.model.Content;
 import org.apache.commons.scxml2.model.Data;
 import org.apache.commons.scxml2.model.Datamodel;
 import org.apache.commons.scxml2.model.Else;
@@ -73,6 +74,7 @@ import org.apache.commons.scxml2.model.T
 import org.apache.commons.scxml2.model.TransitionTarget;
 import org.apache.commons.scxml2.model.Var;
 import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 /**
  * <p>Utility class for serializing the Commons SCXML Java object
@@ -128,7 +130,7 @@ public class SCXMLWriter {
     //---- ELEMENT NAMES ----//
     private static final String ELEM_ASSIGN = "assign";
     private static final String ELEM_CANCEL = "cancel";
-    //private static final String ELEM_CONTENT = "content"; TODO
+    private static final String ELEM_CONTENT = "content";
     private static final String ELEM_DATA = "data";
     private static final String ELEM_DATAMODEL = "datamodel";
     private static final String ELEM_ELSE = "else";
@@ -155,6 +157,7 @@ public class SCXMLWriter {
 
     //---- ATTRIBUTE NAMES ----//
     private static final String ATTR_ARRAY = "array";
+    private static final String ATTR_ATTR = "attr";
     private static final String ATTR_AUTOFORWARD = "autoforward";
     private static final String ATTR_COND = "cond";
     private static final String ATTR_DATAMODEL = "datamodel";
@@ -490,7 +493,7 @@ public class SCXMLWriter {
         // Attributes
         writeAV(writer, ATTR_VERSION, scxml.getVersion());
         writeAV(writer, ATTR_INITIAL, scxml.getInitial());
-        writeAV(writer, ATTR_DATAMODEL, scxml.getDatamodelType());
+        writeAV(writer, ATTR_DATAMODEL, scxml.getDatamodelName());
         writeAV(writer, ATTR_NAME, scxml.getName());
         writeAV(writer, ATTR_PROFILE, scxml.getProfile());
         writeAV(writer, ATTR_EXMODE, scxml.getExmode());
@@ -811,13 +814,15 @@ public class SCXMLWriter {
         writeAV(writer, ATTR_TYPE, invoke.getType());
         writeAV(writer, ATTR_AUTOFORWARD, invoke.getAutoForward());
 
-        for (Param p : invoke.params()) {
+        for (Param p : invoke.getParams()) {
             writer.writeStartElement(ELEM_PARAM);
             writeAV(writer, ATTR_NAME, p.getName());
+            writeAV(writer, ATTR_LOCATION, p.getLocation());
             writeAV(writer, ATTR_EXPR, escapeXML(p.getExpr()));
             writer.writeEndElement();
         }
         writeFinalize(writer, invoke.getFinalize());
+        writeContent(writer, invoke.getContent());
 
         writer.writeEndElement();
     }
@@ -860,7 +865,10 @@ public class SCXMLWriter {
                 Assign asn = (Assign) a;
                 writer.writeStartElement(XMLNS_SCXML, ELEM_ASSIGN);
                 writeAV(writer, ATTR_LOCATION, asn.getLocation());
-                writeAV(writer, ATTR_NAME, asn.getName());
+                if (asn.getType() != null) {
+                    writeAV(writer, ATTR_TYPE, asn.getType().value());
+                }
+                writeAV(writer, ATTR_ATTR, asn.getAttr());
                 writeAV(writer, ATTR_SRC, asn.getSrc());
                 writeAV(writer, ATTR_EXPR, escapeXML(asn.getExpr()));
                 writer.writeEndElement();
@@ -935,7 +943,14 @@ public class SCXMLWriter {
         writeAV(writer, ATTR_NAMELIST, send.getNamelist());
         writeAV(writer, ATTR_HINTS, send.getHints());
 
-        writeExternalContent(writer, send);
+        for (Param p : send.getParams()) {
+            writer.writeStartElement(ELEM_PARAM);
+            writeAV(writer, ATTR_NAME, p.getName());
+            writeAV(writer, ATTR_LOCATION, p.getLocation());
+            writeAV(writer, ATTR_EXPR, escapeXML(p.getExpr()));
+            writer.writeEndElement();
+        }
+        writeContent(writer, send.getContent());
 
         writer.writeEndElement();
     }
@@ -977,6 +992,40 @@ public class SCXMLWriter {
     }
 
     /**
+     * Write the {@link Content} element.
+     *
+     * @param writer The {@link XMLStreamWriter} in use for the serialization.
+     * @param content The content element to write.
+     *
+     * @throws XMLStreamException An exception processing the underlying 
{@link XMLStreamWriter}.
+     */
+    private static void writeContent(final XMLStreamWriter writer, final 
Content content)
+            throws XMLStreamException {
+
+        if (content != null) {
+            writer.writeStartElement(ELEM_CONTENT);
+            writeAV(writer, ATTR_EXPR, content.getExpr());
+            if (content.getBody() != null) {
+                if (content.getBody() instanceof Node) {
+                    NodeList nodeList = 
((Node)content.getBody()).getChildNodes();
+                    if (nodeList.getLength() > 0 && XFORMER == null) {
+                        writer.writeComment("External content was not 
serialized");
+                    }
+                    else {
+                        for (int i = 0, size = nodeList.getLength(); i < size; 
i++) {
+                            writeNode(writer, nodeList.item(i));
+                        }
+                    }
+                }
+                else {
+                    writer.writeCharacters(content.getBody().toString());
+                }
+            }
+            writer.writeEndElement();
+        }
+    }
+
+    /**
      * Write the serialized body of this {@link ExternalContent} element.
      *
      * @param writer The {@link XMLStreamWriter} in use for the serialization.

Modified: 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Action.java
URL: 
http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Action.java?rev=1639829&r1=1639828&r2=1639829&view=diff
==============================================================================
--- 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Action.java
 (original)
+++ 
commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Action.java
 Sat Nov 15 03:07:58 2014
@@ -22,6 +22,7 @@ import java.util.Map;
 import org.apache.commons.scxml2.ActionExecutionContext;
 import org.apache.commons.scxml2.Context;
 import org.apache.commons.scxml2.SCXMLExpressionException;
+import org.w3c.dom.Node;
 
 /**
  * An abstract base class for executable elements in SCXML,
@@ -96,7 +97,7 @@ public abstract class Action implements 
      *
      * @since 0.9
      */
-    public final EnterableState getParentEnterableState()
+    public EnterableState getParentEnterableState()
     throws ModelException {
         if (parent == null && this instanceof Script && 
((Script)this).isGlobalScript()) {
             // global script doesn't have a EnterableState
@@ -113,7 +114,7 @@ public abstract class Action implements 
             return ((History)tt).getParent();
         } else {
             throw new ModelException("Unknown TransitionTarget subclass:"
-                    + tt.getClass().getName());
+                    + (tt != null ? tt.getClass().getName() : "(null)"));
         }
     }
 
@@ -139,5 +140,17 @@ public abstract class Action implements 
         return Context.NAMESPACES_KEY;
     }
 
+    /**
+     * Convenient method to convert a possible {@link Node} result from an 
expression evaluation to a String
+     * using its {@link Node#getTextContent()} method.
+     * @param result the result to convert
+     * @return its text content if the result is a {@link Node} otherwise the 
unmodified result itself
+     */
+    protected Object getTextContentIfNodeResult(final Object result) {
+        if (result instanceof Node) {
+            return ((Node)result).getTextContent();
+        }
+        return result;
+    }
 }
 


Reply via email to