commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From a..@apache.org
Subject svn commit: r1561915 - in /commons/proper/scxml/trunk/src: main/java/org/apache/commons/scxml2/io/ main/java/org/apache/commons/scxml2/model/ test/java/org/apache/commons/scxml2/env/jexl/ test/java/org/apache/commons/scxml2/io/
Date Tue, 28 Jan 2014 00:53:42 GMT
Author: ate
Date: Tue Jan 28 00:53:42 2014
New Revision: 1561915

URL: http://svn.apache.org/r1561915
Log:
SCXML-191: Support foreach element

Added:
    commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ActionsContainer.java   (with props)
    commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Foreach.java   (with props)
    commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/ForeachTest.java   (with props)
    commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/foreach.xml   (with props)
Modified:
    commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java
    commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/io/SCXMLWriter.java
    commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/If.java
    commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/io/SCXMLRequiredAttributesTest.java

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=1561915&r1=1561914&r2=1561915&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 Tue Jan 28 00:53:42 2014
@@ -53,6 +53,7 @@ import org.apache.commons.scxml2.SCXMLHe
 import org.apache.commons.scxml2.env.SimpleErrorHandler;
 import org.apache.commons.scxml2.env.URLResolver;
 import org.apache.commons.scxml2.model.Action;
+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.CustomAction;
@@ -66,6 +67,7 @@ import org.apache.commons.scxml2.model.E
 import org.apache.commons.scxml2.model.ExternalContent;
 import org.apache.commons.scxml2.model.Final;
 import org.apache.commons.scxml2.model.Finalize;
+import org.apache.commons.scxml2.model.Foreach;
 import org.apache.commons.scxml2.model.History;
 import org.apache.commons.scxml2.model.If;
 import org.apache.commons.scxml2.model.Initial;
@@ -229,6 +231,7 @@ public final class SCXMLReader {
     private static final String ELEM_IF = "if";
     private static final String ELEM_INITIAL = "initial";
     private static final String ELEM_INVOKE = "invoke";
+    private static final String ELEM_FOREACH = "foreach";
     private static final String ELEM_LOG = "log";
     private static final String ELEM_ONENTRY = "onentry";
     private static final String ELEM_ONEXIT = "onexit";
@@ -243,6 +246,7 @@ public final class SCXMLReader {
     private static final String ELEM_VAR = "var";
 
     //---- ATTRIBUTE NAMES ----//
+    private static final String ATTR_ARRAY = "array";
     private static final String ATTR_COND = "cond";
     private static final String ATTR_DELAY = "delay";
     private static final String ATTR_EVENT = "event";
@@ -251,7 +255,9 @@ public final class SCXMLReader {
     private static final String ATTR_FINAL = "final";
     private static final String ATTR_HINTS = "hints";
     private static final String ATTR_ID = "id";
+    private static final String ATTR_INDEX = "index";
     private static final String ATTR_INITIAL = "initial";
+    private static final String ATTR_ITEM = "item";
     private static final String ATTR_LABEL = "label";
     private static final String ATTR_LOCATION = "location";
     private static final String ATTR_NAME = "name";
@@ -1414,19 +1420,19 @@ public final class SCXMLReader {
      * @param configuration The {@link Configuration} to use while parsing.
      * @param tt The parent {@link TransitionTarget} for this executable content.
      * @param executable The parent {@link Executable} to which this content belongs.
-     * @param iff The optional parent {@link If} if this is child content of an if action.
+     * @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 readExecutableContext(final XMLStreamReader reader, final Configuration configuration,
-            final TransitionTarget tt, final Executable executable, final If iff)
+            final TransitionTarget tt, final Executable executable, final ActionsContainer parent)
     throws XMLStreamException, ModelException {
 
         String end = "";
-        if (iff != null) {
-            end = ELEM_IF;
+        if (parent != null) {
+            end = parent.getContainerElementName();
         } else if (executable instanceof Transition) {
             end = ELEM_TRANSITION;
         } else if (executable instanceof OnEntry) {
@@ -1446,33 +1452,35 @@ public final class SCXMLReader {
                     name = reader.getLocalName();
                     if (XMLNS_SCXML.equals(nsURI)) {
                         if (ELEM_EVENT.equals(name)) {
-                            readEvent(reader, configuration, tt, executable, iff);
+                            readEvent(reader, configuration, tt, executable, parent);
+                        } else if (ELEM_FOREACH.equals(name)) {
+                            readForeach(reader, configuration, tt, executable, parent);
                         } else if (ELEM_IF.equals(name)) {
-                            readIf(reader, configuration, tt, executable, iff);
+                            readIf(reader, configuration, tt, executable, parent);
                         } else if (ELEM_LOG.equals(name)) {
-                            readLog(reader, configuration, tt, executable, iff);
+                            readLog(reader, configuration, tt, executable, parent);
                         } else if (ELEM_ASSIGN.equals(name)) {
-                            readAssign(reader, configuration, tt, executable, iff);
+                            readAssign(reader, configuration, tt, executable, parent);
                         } else if (ELEM_VALIDATE.equals(name)) {
-                            readValidate(reader, configuration, tt, executable, iff);
+                            readValidate(reader, configuration, tt, executable, parent);
                         } else if (ELEM_SEND.equals(name)) {
-                            readSend(reader, configuration, tt, executable, iff);
+                            readSend(reader, configuration, tt, executable, parent);
                         } else if (ELEM_CANCEL.equals(name)) {
-                            readCancel(reader, configuration, tt, executable, iff);
+                            readCancel(reader, configuration, tt, executable, parent);
                         } else if (ELEM_SCRIPT.equals(name)) {
-                            readScript(reader, configuration, tt, executable, iff);
+                            readScript(reader, configuration, tt, executable, parent);
                         } else if (ELEM_IF.equals(end) && ELEM_ELSEIF.equals(name)) {
-                            readElseIf(reader, configuration, tt, executable, iff);
+                            readElseIf(reader, configuration, tt, executable, (If) parent);
                         } else if (ELEM_IF.equals(end) && ELEM_ELSE.equals(name)) {
-                            readElse(reader, configuration, tt, executable, iff);
+                            readElse(reader, configuration, tt, executable, (If)parent);
                         } else {
                             reportIgnoredElement(reader, configuration, end, nsURI, name);
                         }
                     } else if (XMLNS_COMMONS_SCXML.equals(nsURI)) {
                         if (ELEM_VAR.equals(name)) {
-                            readVar(reader, configuration, tt, executable, iff);
+                            readVar(reader, configuration, tt, executable, parent);
                         } else if (ELEM_EXIT.equals(name)) {
-                            readExit(reader, configuration, tt, executable, iff);
+                            readExit(reader, configuration, tt, executable, parent);
                         } else {
                             reportIgnoredElement(reader, configuration, end, nsURI, name);
                         }
@@ -1486,7 +1494,7 @@ public final class SCXMLReader {
                             }
                         }
                         if (customAction != null) {
-                            readCustomAction(reader, configuration, customAction, tt, executable, iff);
+                            readCustomAction(reader, configuration, customAction, tt, executable, parent);
                         } else {
                             reportIgnoredElement(reader, configuration, end, nsURI, name);
                         }
@@ -1513,20 +1521,20 @@ public final class SCXMLReader {
      * @param configuration The {@link Configuration} to use while parsing.
      * @param tt The parent {@link TransitionTarget} for this action.
      * @param executable The parent {@link Executable} for this action.
-     * @param iff The optional parent {@link If} if this action is a child of one.
+     * @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 readEvent(final XMLStreamReader reader, final Configuration configuration,
-            final TransitionTarget tt, final Executable executable, final If iff)
+            final TransitionTarget tt, final Executable executable, final ActionsContainer parent)
     throws XMLStreamException {
 
         Event event = new Event();
         event.setName(readAV(reader, ATTR_NAME));
         readNamespaces(configuration, event);
         event.setParent(executable);
-        if (iff != null) {
-            iff.addAction(event);
+        if (parent != null) {
+            parent.addAction(event);
         } else {
             executable.addAction(event);
         }
@@ -1540,14 +1548,14 @@ public final class SCXMLReader {
      * @param configuration The {@link Configuration} to use while parsing.
      * @param tt The parent {@link TransitionTarget} for this action.
      * @param executable The parent {@link Executable} for this action.
-     * @param parent The optional parent {@link If} if this <if> is a child of another.
+     * @param parent The optional parent {@link ActionsContainer} if this <if> 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 readIf(final XMLStreamReader reader, final Configuration configuration,
-            final TransitionTarget tt, final Executable executable, final If parent)
+            final TransitionTarget tt, final Executable executable, final ActionsContainer parent)
     throws XMLStreamException, ModelException {
 
         If iff = new If();
@@ -1609,18 +1617,50 @@ public final class SCXMLReader {
     }
 
     /**
+     * Read the contents of this <foreach> element.
+     *
+     * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
+     * @param configuration The {@link Configuration} to use while parsing.
+     * @param tt The parent {@link TransitionTarget} for this action.
+     * @param executable The parent {@link Executable} for this action.
+     * @param parent The optional parent {@link ActionsContainer} if this <foreach> 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 readForeach(final XMLStreamReader reader, final Configuration configuration,
+                                    final TransitionTarget tt, final Executable executable, final ActionsContainer parent)
+            throws XMLStreamException, ModelException {
+
+        Foreach fe = new Foreach();
+        fe.setArray(readRequiredAV(reader, ELEM_FOREACH, ATTR_ARRAY));
+        fe.setItem(readRequiredAV(reader, ELEM_FOREACH, ATTR_ITEM));
+        fe.setIndex(readAV(reader, ATTR_INDEX));
+        readNamespaces(configuration, fe);
+        fe.setParent(executable);
+        if (parent != null) {
+            parent.addAction(fe);
+        } else {
+            executable.addAction(fe);
+        }
+        readExecutableContext(reader, configuration, tt, executable, fe);
+
+    }
+
+    /**
      * Read the contents of this <log> element.
      *
      * @param reader The {@link XMLStreamReader} providing the SCXML document to parse.
      * @param configuration The {@link Configuration} to use while parsing.
      * @param tt The parent {@link TransitionTarget} for this action.
      * @param executable The parent {@link Executable} for this action.
-     * @param iff The optional parent {@link If} if this action is a child of one.
+     * @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 Configuration configuration,
-            final TransitionTarget tt, final Executable executable, final If iff)
+            final TransitionTarget tt, final Executable executable, final ActionsContainer parent)
     throws XMLStreamException {
 
         Log log = new Log();
@@ -1628,8 +1668,8 @@ public final class SCXMLReader {
         log.setLabel(readAV(reader, ATTR_LABEL));
         readNamespaces(configuration, log);
         log.setParent(executable);
-        if (iff != null) {
-            iff.addAction(log);
+        if (parent != null) {
+            parent.addAction(log);
         } else {
             executable.addAction(log);
         }
@@ -1643,31 +1683,30 @@ public final class SCXMLReader {
      * @param configuration The {@link Configuration} to use while parsing.
      * @param tt The parent {@link TransitionTarget} for this action.
      * @param executable The parent {@link Executable} for this action.
-     * @param iff The optional parent {@link If} if this action is a child of one.
+     * @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 readAssign(final XMLStreamReader reader, final Configuration configuration,
-            final TransitionTarget tt, final Executable executable, final If iff)
+            final TransitionTarget tt, final Executable executable, final ActionsContainer parent)
     throws XMLStreamException, ModelException {
 
         Assign assign = new Assign();
         assign.setExpr(readAV(reader, ATTR_EXPR));
         assign.setName(readAV(reader, ATTR_NAME));
-        if (SCXMLHelper.isStringEmpty(assign.getName())) {
-            // if custom attribute name not specified or empty, enforce location attribute to be specified
-            assign.setLocation(readRequiredAV(reader, ELEM_ASSIGN, ATTR_LOCATION));
+        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));
         }
         else {
-            assign.setLocation(readAV(reader, ATTR_LOCATION));
+            assign.setLocation(readRequiredAV(reader, ELEM_ASSIGN, ATTR_LOCATION));
         }
-        assign.setLocation(readAV(reader, ATTR_LOCATION));
         assign.setSrc(readAV(reader, ATTR_SRC));
         assign.setPathResolver(configuration.pathResolver);
         readNamespaces(configuration, assign);
         assign.setParent(executable);
-        if (iff != null) {
-            iff.addAction(assign);
+        if (parent != null) {
+            parent.addAction(assign);
         } else {
             executable.addAction(assign);
         }
@@ -1681,12 +1720,12 @@ public final class SCXMLReader {
      * @param configuration The {@link Configuration} to use while parsing.
      * @param tt The parent {@link TransitionTarget} for this action.
      * @param executable The parent {@link Executable} for this action.
-     * @param iff The optional parent {@link If} if this action is a child of one.
+     * @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 readValidate(final XMLStreamReader reader, final Configuration configuration,
-            final TransitionTarget tt, final Executable executable, final If iff)
+            final TransitionTarget tt, final Executable executable, final ActionsContainer parent)
     throws XMLStreamException {
 
         // TODO validate support
@@ -1700,12 +1739,12 @@ public final class SCXMLReader {
      * @param configuration The {@link Configuration} to use while parsing.
      * @param tt The parent {@link TransitionTarget} for this action.
      * @param executable The parent {@link Executable} for this action.
-     * @param iff The optional parent {@link If} if this action is a child of one.
+     * @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 readSend(final XMLStreamReader reader, final Configuration configuration,
-            final TransitionTarget tt, final Executable executable, final If iff)
+            final TransitionTarget tt, final Executable executable, final ActionsContainer parent)
     throws XMLStreamException {
 
         Send send = new Send();
@@ -1726,8 +1765,8 @@ public final class SCXMLReader {
         }
 
         send.setParent(executable);
-        if (iff != null) {
-            iff.addAction(send);
+        if (parent != null) {
+            parent.addAction(send);
         } else {
             executable.addAction(send);
         }
@@ -1741,19 +1780,19 @@ public final class SCXMLReader {
      * @param configuration The {@link Configuration} to use while parsing.
      * @param tt The parent {@link TransitionTarget} for this action.
      * @param executable The parent {@link Executable} for this action.
-     * @param iff The optional parent {@link If} if this action is a child of one.
+     * @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 readCancel(final XMLStreamReader reader, final Configuration configuration,
-            final TransitionTarget tt, final Executable executable, final If iff)
+            final TransitionTarget tt, final Executable executable, final ActionsContainer parent)
     throws XMLStreamException {
 
         Cancel cancel = new Cancel();
         readNamespaces(configuration, cancel);
         cancel.setParent(executable);
-        if (iff != null) {
-            iff.addAction(cancel);
+        if (parent != null) {
+            parent.addAction(cancel);
         } else {
             executable.addAction(cancel);
         }
@@ -1767,20 +1806,20 @@ public final class SCXMLReader {
      * @param configuration The {@link Configuration} to use while parsing.
      * @param tt The parent {@link TransitionTarget} for this action.
      * @param executable The parent {@link Executable} for this action.
-     * @param iff The optional parent {@link If} if this action is a child of one.
+     * @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 readScript(final XMLStreamReader reader, final Configuration configuration,
-            final TransitionTarget tt, final Executable executable, final If iff)
+            final TransitionTarget tt, final Executable executable, final ActionsContainer parent)
     throws XMLStreamException {
 
         Script script = new Script();
         readNamespaces(configuration, script);
         script.setBody(readBody(reader, configuration, XMLNS_SCXML, ELEM_SCRIPT));
         script.setParent(executable);
-        if (iff != null) {
-            iff.addAction(script);
+        if (parent != null) {
+            parent.addAction(script);
         } else {
             executable.addAction(script);
         }
@@ -1821,12 +1860,12 @@ public final class SCXMLReader {
      * @param configuration The {@link Configuration} to use while parsing.
      * @param tt The parent {@link TransitionTarget} for this action.
      * @param executable The parent {@link Executable} for this action.
-     * @param iff The optional parent {@link If} if this action is a child of one.
+     * @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 readVar(final XMLStreamReader reader, final Configuration configuration,
-            final TransitionTarget tt, final Executable executable, final If iff)
+            final TransitionTarget tt, final Executable executable, final ActionsContainer parent)
     throws XMLStreamException {
 
         Var var = new Var();
@@ -1834,8 +1873,8 @@ public final class SCXMLReader {
         var.setExpr(readAV(reader, ATTR_EXPR));
         readNamespaces(configuration, var);
         var.setParent(executable);
-        if (iff != null) {
-            iff.addAction(var);
+        if (parent != null) {
+            parent.addAction(var);
         } else {
             executable.addAction(var);
         }
@@ -1850,19 +1889,19 @@ public final class SCXMLReader {
      * @param configuration The {@link Configuration} to use while parsing.
      * @param tt The parent {@link TransitionTarget} for this action.
      * @param executable The parent {@link Executable} for this action.
-     * @param iff The optional parent {@link If} if this action is a child of one.
+     * @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 readExit(final XMLStreamReader reader, final Configuration configuration,
-            final TransitionTarget tt, final Executable executable, final If iff)
+            final TransitionTarget tt, final Executable executable, final ActionsContainer parent)
     throws XMLStreamException {
 
         Exit exit = new Exit();
         readNamespaces(configuration, exit);
         exit.setParent(executable);
-        if (iff != null) {
-            iff.addAction(exit);
+        if (parent != null) {
+            parent.addAction(exit);
         } else {
             executable.addAction(exit);
         }
@@ -1877,13 +1916,13 @@ public final class SCXMLReader {
      * @param customAction The {@link CustomAction} to read.
      * @param tt The parent {@link TransitionTarget} for this custom action.
      * @param executable The parent {@link Executable} for this custom action.
-     * @param iff The optional parent {@link If} if this custom action is a child of one.
+     * @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}.
      */
     private static void readCustomAction(final XMLStreamReader reader, final Configuration configuration,
             final CustomAction customAction, final TransitionTarget tt, final Executable executable,
-            final If iff)
+            final ActionsContainer parent)
     throws XMLStreamException {
 
         // Instantiate custom action
@@ -1947,8 +1986,8 @@ public final class SCXMLReader {
         // Wire in the action and add to parent
         readNamespaces(configuration, action);
         action.setParent(executable);
-        if (iff != null) {
-            iff.addAction(action);
+        if (parent != null) {
+            parent.addAction(action);
         } else {
             executable.addAction(action);
         }
@@ -2707,8 +2746,6 @@ public final class SCXMLReader {
         public void setStrict(boolean strict) {
             this.strict = strict;
         }
-
     }
-
 }
 

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=1561915&r1=1561914&r2=1561915&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 Tue Jan 28 00:53:42 2014
@@ -55,6 +55,7 @@ import org.apache.commons.scxml2.model.E
 import org.apache.commons.scxml2.model.ExternalContent;
 import org.apache.commons.scxml2.model.Final;
 import org.apache.commons.scxml2.model.Finalize;
+import org.apache.commons.scxml2.model.Foreach;
 import org.apache.commons.scxml2.model.History;
 import org.apache.commons.scxml2.model.If;
 import org.apache.commons.scxml2.model.Initial;
@@ -141,6 +142,7 @@ public class SCXMLWriter {
     private static final String ELEM_IF = "if";
     private static final String ELEM_INITIAL = "initial";
     private static final String ELEM_INVOKE = "invoke";
+    private static final String ELEM_FOREACH = "foreach";
     private static final String ELEM_LOG = "log";
     private static final String ELEM_ONENTRY = "onentry";
     private static final String ELEM_ONEXIT = "onexit";
@@ -155,6 +157,7 @@ public class SCXMLWriter {
     private static final String ELEM_VAR = "var";
 
     //---- ATTRIBUTE NAMES ----//
+    private static final String ATTR_ARRAY = "array";
     private static final String ATTR_COND = "cond";
     private static final String ATTR_DELAY = "delay";
     private static final String ATTR_EVENT = "event";
@@ -163,7 +166,9 @@ public class SCXMLWriter {
     private static final String ATTR_FINAL = "final";
     private static final String ATTR_HINTS = "hints";
     private static final String ATTR_ID = "id";
+    private static final String ATTR_INDEX = "index";
     private static final String ATTR_INITIAL = "initial";
+    private static final String ATTR_ITEM = "item";
     private static final String ATTR_LABEL = "label";
     private static final String ATTR_LOCATION = "location";
     private static final String ATTR_NAME = "name";
@@ -792,6 +797,8 @@ public class SCXMLWriter {
                 writer.writeStartElement(XMLNS_SCXML, ELEM_CANCEL);
                 writeAV(writer, ATTR_SENDID, c.getSendid());
                 writer.writeEndElement();
+            } else if (a instanceof Foreach) {
+                writeForeach(writer, (Foreach) a);
             } else if (a instanceof Log) {
                 Log lg = (Log) a;
                 writer.writeStartElement(XMLNS_SCXML, ELEM_LOG);
@@ -874,12 +881,31 @@ public class SCXMLWriter {
     throws XMLStreamException {
 
         writer.writeStartElement(ELEM_IF);
-        writeAV(writer, ATTR_COND, iff.getCond());
+        writeAV(writer, ATTR_COND, SCXMLHelper.escapeXML(iff.getCond()));
         writeExecutableContent(writer, iff.getActions());
         writer.writeEndElement();
     }
 
     /**
+     * Write out this {@link Foreach} object into its serialization as the corresponding <foreach> element.
+     *
+     * @param writer The {@link XMLStreamWriter} in use for the serialization.
+     * @param foreach The {@link If} to serialize.
+     *
+     * @throws XMLStreamException An exception processing the underlying {@link XMLStreamWriter}.
+     */
+    private static void writeForeach(final XMLStreamWriter writer, final Foreach foreach)
+            throws XMLStreamException {
+
+        writer.writeStartElement(ELEM_FOREACH);
+        writeAV(writer, ATTR_ITEM, foreach.getItem());
+        writeAV(writer, ATTR_INDEX, foreach.getIndex());
+        writeAV(writer, ATTR_ARRAY, SCXMLHelper.escapeXML(foreach.getArray()));
+        writeExecutableContent(writer, foreach.getActions());
+        writer.writeEndElement();
+    }
+
+    /**
      * Write the serialized body of this {@link ExternalContent} element.
      *
      * @param writer The {@link XMLStreamWriter} in use for the serialization.

Added: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ActionsContainer.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ActionsContainer.java?rev=1561915&view=auto
==============================================================================
--- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ActionsContainer.java (added)
+++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ActionsContainer.java Tue Jan 28 00:53:42 2014
@@ -0,0 +1,51 @@
+/*
+ * 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.model;
+
+import java.util.List;
+
+/**
+ * An <code>ActionsContainer</code> is an entity that holds a list of <code>Action</code> elements.
+ */
+public interface ActionsContainer {
+
+    /** The <if> ActionsContainer element name */
+    String ELEM_IF = "if";
+    /** The <foreach> ActionsContainer element name */
+    String ELEM_FOREACH = "foreach";
+
+    /**
+     * Get the Document element type for this &lt;container&gt;.
+     *
+     * @return Returns the element type
+     */
+    String getContainerElementName();
+    /**
+     * Get the executable actions contained in this &lt;container&gt;.
+     *
+     * @return Returns the actions.
+     */
+    List<Action> getActions();
+
+    /**
+     * Add an Action to the list of executable actions contained in
+     * this &lt;container&gt;.
+     *
+     * @param action The action to add.
+     */
+    void addAction(final Action action);
+}

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

Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ActionsContainer.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Foreach.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Foreach.java?rev=1561915&view=auto
==============================================================================
--- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Foreach.java (added)
+++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Foreach.java Tue Jan 28 00:53:42 2014
@@ -0,0 +1,156 @@
+/*
+ * 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.model;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.scxml2.Context;
+import org.apache.commons.scxml2.ErrorReporter;
+import org.apache.commons.scxml2.Evaluator;
+import org.apache.commons.scxml2.EventDispatcher;
+import org.apache.commons.scxml2.SCInstance;
+import org.apache.commons.scxml2.SCXMLExpressionException;
+import org.apache.commons.scxml2.TriggerEvent;
+
+/**
+ * The class in this SCXML object model that corresponds to the
+ * &lt;foreach&gt; SCXML element, which allows an SCXML application to iterate through a collection in the data model
+ * and to execute the actions contained within it for each item in the collection.
+ */
+public class Foreach extends Action implements ActionsContainer {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = 1L;
+
+    private String array;
+    private String item;
+    private String index;
+
+    /**
+     * The set of executable elements (those that inheriting from
+     * Action) that are contained in this &lt;if&gt; element.
+     */
+    private List<Action> actions;
+
+    public Foreach() {
+        super();
+        this.actions = new ArrayList<Action>();
+    }
+
+    @Override
+    public final String getContainerElementName() {
+        return ELEM_FOREACH;
+    }
+
+    @Override
+    public final List<Action> getActions() {
+        return actions;
+    }
+
+    @Override
+    public final void addAction(final Action action) {
+        if (action != null) {
+            this.actions.add(action);
+        }
+    }
+
+    public String getArray() {
+        return array;
+    }
+
+    public void setArray(final String array) {
+        this.array = array;
+    }
+
+    public String getItem() {
+        return item;
+    }
+
+    public void setItem(final String item) {
+        this.item = item;
+    }
+
+    public String getIndex() {
+        return index;
+    }
+
+    public void setIndex(final String index) {
+        this.index = index;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void execute(final EventDispatcher evtDispatcher,
+                        final ErrorReporter errRep, final SCInstance scInstance,
+                        final Log appLog, final Collection<TriggerEvent> derivedEvents)
+            throws ModelException, SCXMLExpressionException {
+        Context ctx = scInstance.getContext(getParentTransitionTarget());
+        Evaluator eval = scInstance.getEvaluator();
+        ctx.setLocal(getNamespacesKey(), getNamespaces());
+        try {
+            Object arrayObject = eval.eval(ctx,array);
+            if (arrayObject != null && arrayObject instanceof Iterable || arrayObject.getClass().isArray()) {
+                if (arrayObject.getClass().isArray()) {
+                    for (int currentIndex = 0, size = Array.getLength(arrayObject); currentIndex < size; currentIndex++) {
+                        ctx.setLocal(item, Array.get(arrayObject, currentIndex));
+                        ctx.setLocal(index, currentIndex);
+                        // The "foreach" statement is a "container"
+                        for (Action aa : actions) {
+                            aa.execute(evtDispatcher, errRep, scInstance, appLog, derivedEvents);
+                        }
+                    }
+                }
+                else {
+                    // Spec requires to iterate over a shallow copy of underlying array in a way that modifications to
+                    // the collection during the execution of <foreach> must not affect the iteration behavior.
+                    // For array objects (see above) this isn't needed, but for Iterables we don't have that guarantee
+                    // so we make a copy first
+                    ArrayList<Object> arrayList = new ArrayList<Object>();
+                    for (Object value: (Iterable)arrayObject) {
+                        arrayList.add(value);
+                    }
+                    int currentIndex = 0;
+                    for (Object value : arrayList) {
+                        ctx.setLocal(item, value);
+                        if (index != null) {
+                            ctx.setLocal(index, currentIndex);
+                        }
+                        // The "foreach" statement is a "container"
+                        for (Action aa : actions) {
+                            aa.execute(evtDispatcher, errRep, scInstance, appLog, derivedEvents);
+                        }
+                        currentIndex++;
+                    }
+                }
+            }
+            else {
+                // TODO: place the error 'error.execution' in the internal event queue. (section "3.12.2 Errors")
+            }
+        }
+        finally {
+            ctx.setLocal(getNamespacesKey(), null);
+        }
+    }
+}

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

Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Foreach.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/If.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/If.java?rev=1561915&r1=1561914&r2=1561915&view=diff
==============================================================================
--- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/If.java (original)
+++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/If.java Tue Jan 28 00:53:42 2014
@@ -38,7 +38,7 @@ import org.apache.commons.scxml2.semanti
  * the elements within an &lt;if&gt;.
  *
  */
-public class If extends Action {
+public class If extends Action implements ActionsContainer {
 
     /**
      * Serial version UID.
@@ -71,6 +71,11 @@ public class If extends Action {
         this.execute = false;
     }
 
+    @Override
+    public final String getContainerElementName() {
+        return ELEM_IF;
+    }
+
     /**
      * Get the executable actions contained in this &lt;if&gt;.
      *

Added: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/ForeachTest.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/ForeachTest.java?rev=1561915&view=auto
==============================================================================
--- commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/ForeachTest.java (added)
+++ commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/ForeachTest.java Tue Jan 28 00:53:42 2014
@@ -0,0 +1,41 @@
+/*
+ * 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.jexl;
+
+import java.net.URL;
+
+import org.apache.commons.scxml2.SCXMLExecutor;
+import org.apache.commons.scxml2.SCXMLTestHelper;
+import org.apache.commons.scxml2.model.SCXML;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Simple test for SCXML <foreach/>
+ */
+public class ForeachTest {
+
+    @Test
+    public void testForeach() throws Exception {
+        URL document = this.getClass().getClassLoader().getResource("org/apache/commons/scxml2/env/jexl/foreach.xml");
+        SCXML scxml = SCXMLTestHelper.parse(document);
+        Assert.assertNotNull(scxml);
+        SCXMLExecutor exec = SCXMLTestHelper.getExecutor(scxml, new JexlContext(), new JexlEvaluator());
+        Assert.assertNotNull(exec);
+        Assert.assertTrue(exec.getCurrentStatus().isFinal());
+    }
+}

Propchange: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/ForeachTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/ForeachTest.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/foreach.xml
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/foreach.xml?rev=1561915&view=auto
==============================================================================
--- commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/foreach.xml (added)
+++ commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/foreach.xml Tue Jan 28 00:53:42 2014
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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.
+-->
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+  <datamodel>
+    <data id="var1" expr="[1,2,3]"/>
+    <data id="var2" expr="0"/>
+  </datamodel>
+
+  <state id="test">
+    <onentry>
+      <foreach item="var3" array="var1">
+        <assign name="var2" expr="var2 + var3"/>
+      </foreach>
+    </onentry>
+    <transition cond="var2==6" target="finished"/>
+  </state>
+  <final id="finished"/>
+</scxml>

Propchange: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/foreach.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/foreach.xml
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/jexl/foreach.xml
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/io/SCXMLRequiredAttributesTest.java
URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/io/SCXMLRequiredAttributesTest.java?rev=1561915&r1=1561914&r2=1561915&view=diff
==============================================================================
--- commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/io/SCXMLRequiredAttributesTest.java (original)
+++ commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/io/SCXMLRequiredAttributesTest.java Tue Jan 28 00:53:42 2014
@@ -130,6 +130,36 @@ public class SCXMLRequiredAttributesTest
                     "  <final id=\"fine\"/>\n" +
                     "</scxml>";
 
+    private static final String SCXML_WITH_MISSING_FOREACH_ARRAY =
+            "<scxml xmlns=\"http://www.w3.org/2005/07/scxml\" version=\"1.0\">\n" +
+                    "  <state id=\"s1\">\n" +
+                    "    <transition target=\"fine\">\n" +
+                    "      <foreach item=\"y\"></foreach>\n" +
+                    "    </transition>\n" +
+                    "  </state>\n" +
+                    "  <final id=\"fine\"/>\n" +
+                    "</scxml>";
+
+    private static final String SCXML_WITH_MISSING_FOREACH_ITEM =
+            "<scxml xmlns=\"http://www.w3.org/2005/07/scxml\" version=\"1.0\">\n" +
+                    "  <state id=\"s1\">\n" +
+                    "    <transition target=\"fine\">\n" +
+                    "      <foreach array=\"[1,2]\"></foreach>\n" +
+                    "    </transition>\n" +
+                    "  </state>\n" +
+                    "  <final id=\"fine\"/>\n" +
+                    "</scxml>";
+
+    private static final String SCXML_WITH_FOREACH =
+            "<scxml xmlns=\"http://www.w3.org/2005/07/scxml\" version=\"1.0\">\n" +
+                    "  <state id=\"s1\">\n" +
+                    "    <transition target=\"fine\">\n" +
+                    "      <foreach array=\"[1,2]\" item=\"x\"></foreach>\n" +
+                    "    </transition>\n" +
+                    "  </state>\n" +
+                    "  <final id=\"fine\"/>\n" +
+                    "</scxml>";
+
     @Test
     public void testValidSCXML() throws Exception {
         SCXML scxml = SCXMLTestHelper.parse(new StringReader(VALID_SCXML), null);
@@ -230,6 +260,36 @@ public class SCXMLRequiredAttributesTest
         // Note: cannot execute this instance without providing proper <invoke> src attribute
     }
 
+    @Test
+    public void testSCXMLMissingForeachArray() throws Exception {
+        try {
+            SCXMLTestHelper.parse(new StringReader(SCXML_WITH_MISSING_FOREACH_ARRAY), null);
+            fail("SCXML reading should have failed due to missing foreach array in SCXML");
+        }
+        catch (ModelException e) {
+            assertTrue(e.getMessage().startsWith("<foreach> is missing required attribute \"array\" value"));
+        }
+    }
+
+    @Test
+    public void testSCXMLMissingForeachItem() throws Exception {
+        try {
+            SCXMLTestHelper.parse(new StringReader(SCXML_WITH_MISSING_FOREACH_ITEM), null);
+            fail("SCXML reading should have failed due to missing foreach item in SCXML");
+        }
+        catch (ModelException e) {
+            assertTrue(e.getMessage().startsWith("<foreach> is missing required attribute \"item\" value"));
+        }
+    }
+
+    @Test
+    public void testSCXMLWithForEach() throws Exception {
+        SCXML scxml = SCXMLTestHelper.parse(new StringReader(SCXML_WITH_FOREACH), null);
+        assertNotNull(scxml);
+        SCXMLExecutor exec = executeSCXML(scxml);
+        assertTrue(exec.getCurrentStatus().isFinal());
+    }
+
     private SCXMLExecutor executeSCXML(SCXML scxml) throws Exception {
         SCXMLExecutor exec = SCXMLTestHelper.getExecutor(scxml, new JexlContext(), new JexlEvaluator());
         exec.go();



Mime
View raw message