Repository: commons-scxml Updated Branches: refs/heads/master b39a4adf6 -> 144d205df
SCXML-266 SCXMLSemantics#matchTransition - code flow bug Project: http://git-wip-us.apache.org/repos/asf/commons-scxml/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-scxml/commit/144d205d Tree: http://git-wip-us.apache.org/repos/asf/commons-scxml/tree/144d205d Diff: http://git-wip-us.apache.org/repos/asf/commons-scxml/diff/144d205d Branch: refs/heads/master Commit: 144d205df2fcceacd3898e9d9afe7cbcd5ed8340 Parents: b39a4ad Author: Ate Douma <[email protected]> Authored: Sun Dec 10 18:22:22 2017 +0100 Committer: Ate Douma <[email protected]> Committed: Sun Dec 10 18:22:22 2017 +0100 ---------------------------------------------------------------------- src/changes/changes.xml | 4 + .../org/apache/commons/scxml2/SCInstance.java | 24 ++++-- .../apache/commons/scxml2/io/SCXMLReader.java | 68 ++++++++++++++- .../apache/commons/scxml2/io/SCXMLWriter.java | 14 +-- .../org/apache/commons/scxml2/model/Assign.java | 91 ++++++++++++-------- .../org/apache/commons/scxml2/w3c/tests.xml | 6 +- 6 files changed, 154 insertions(+), 53 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-scxml/blob/144d205d/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index f3b03ae..3238f53 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -35,6 +35,10 @@ <release version="2.0" date="In Git master" description="Latest unreleased code"> + <action dev="ate" type="add" issue="SCXML-267"> + [12-10-2017] Support <data> src attribute and <assign> inline data + </action> + <action dev="ate" type="fix" issue="SCXML-266"> [12-10-2017] SCXMLSemantics#matchTransition - code flow bug </action> http://git-wip-us.apache.org/repos/asf/commons-scxml/blob/144d205d/src/main/java/org/apache/commons/scxml2/SCInstance.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/scxml2/SCInstance.java b/src/main/java/org/apache/commons/scxml2/SCInstance.java index 5fe715f..12d64e1 100644 --- a/src/main/java/org/apache/commons/scxml2/SCInstance.java +++ b/src/main/java/org/apache/commons/scxml2/SCInstance.java @@ -16,6 +16,7 @@ */ package org.apache.commons.scxml2; +import java.io.IOException; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; @@ -26,6 +27,7 @@ import java.util.Set; import java.util.UUID; import org.apache.commons.scxml2.env.SimpleContext; +import org.apache.commons.scxml2.io.ContentParser; import org.apache.commons.scxml2.model.Data; import org.apache.commons.scxml2.model.Datamodel; import org.apache.commons.scxml2.model.EnterableState; @@ -334,14 +336,24 @@ public class SCInstance implements Serializable { } Object value = null; boolean setValue = false; - /* - TODO: external data.src support (not yet implemented), including late-binding thereof // prefer "src" over "expr" over "inline" if (datum.getSrc() != null) { - ctx.setLocal(datum.getId(), valueNode); - } else - */ - if (datum.getExpr() != null) { + String resolvedSrc = datum.getSrc(); + final PathResolver pr = getStateMachine().getPathResolver(); + if (pr != null) { + resolvedSrc = pr.resolvePath(resolvedSrc); + } + try { + value = ContentParser.DEFAULT_PARSER.parseResource(resolvedSrc); + setValue = true; + } catch (IOException e) { + if (internalIOProcessor != null) { + internalIOProcessor.addEvent(new EventBuilder(TriggerEvent.ERROR_EXECUTION, TriggerEvent.ERROR_EVENT).build()); + } + errorReporter.onError(ErrorConstants.EXECUTION_ERROR, e.getMessage(), datum); + } + } + else if (datum.getExpr() != null) { try { ctx.setLocal(Context.NAMESPACES_KEY, datum.getNamespaces()); value = evaluator.eval(ctx, datum.getExpr()); http://git-wip-us.apache.org/repos/asf/commons-scxml/blob/144d205d/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java b/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java index c9b0349..d5618d1 100644 --- a/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java +++ b/src/main/java/org/apache/commons/scxml2/io/SCXMLReader.java @@ -33,6 +33,7 @@ import java.util.Stack; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.Location; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLReporter; import javax.xml.stream.XMLResolver; @@ -1794,13 +1795,58 @@ public final class SCXMLReader { assign.setLocation(readRequiredAV(reader, ELEM_ASSIGN, ATTR_LOCATION)); assign.setSrc(readAV(reader, ATTR_SRC)); readNamespaces(configuration, assign); + if (assign.getExpr() != null && assign.getSrc() != null) { + reportConflictingAttribute(reader, configuration, ELEM_ASSIGN, ATTR_EXPR, ATTR_SRC); + } + else if (assign.getExpr() != null) { + skipToEndElement(reader); + } + else if (assign.getSrc() != null) { + skipToEndElement(reader); + String resolvedSrc = assign.getSrc(); + if (configuration.pathResolver != null) { + resolvedSrc = configuration.pathResolver.resolvePath(resolvedSrc); + } + try { + assign.setValue(ContentParser.DEFAULT_PARSER.parseResource(resolvedSrc)); + } catch (IOException e) { + throw new ModelException(e); + } + } + else { + Location location = reader.getLocation(); + Node node = readNode(reader, configuration, XMLNS_SCXML, ELEM_ASSIGN, new String[]{ATTR_LOCATION}); + if (node.hasChildNodes()) { + assign.setNode(node); + NodeList children = node.getChildNodes(); + if (children.getLength() == 1 && children.item(0).getNodeType() == Node.TEXT_NODE) { + String text = configuration.contentParser.trimContent(children.item(0).getNodeValue()); + if (configuration.contentParser.hasJsonSignature(text)) { + try { + assign.setValue(configuration.contentParser.parseJson(text)); + } catch (IOException e) { + throw new ModelException(e); + } + } + else { + assign.setValue(configuration.contentParser.spaceNormalizeContent(text)); + } + } else { + // can only handle a single node: pick the first child + assign.setValue(children.item(0)); + } + } else { + // report missing expression (as most common use-case) + reportMissingAttribute(location, ELEM_ASSIGN, ATTR_EXPR); + } + } + assign.setParent(executable); if (parent != null) { parent.addAction(assign); } else { executable.addAction(assign); } - skipToEndElement(reader); } /** @@ -2348,9 +2394,7 @@ public final class SCXMLReader { throws ModelException { String value = nullIfEmpty(reader.getAttributeValue(XMLNS_DEFAULT, attrLocalName)); if (value == null) { - MessageFormat msgFormat = new MessageFormat(ERR_REQUIRED_ATTRIBUTE_MISSING); - String errMsg = msgFormat.format(new Object[] {elementName, attrLocalName, reader.getLocation()}); - throw new ModelException(errMsg); + reportMissingAttribute(reader.getLocation(), elementName, attrLocalName); } return value; } @@ -2382,6 +2426,22 @@ public final class SCXMLReader { } /** + * Report a missing required attribute value at the current reader location, + * + * @param location The (approximate) {@link Location} where the attribute is expected. + * @param elementName The name of the element for which the attribute value is needed. + * @param attrLocalName The attribute name whose value is needed. + * + * @throws ModelException The required attribute is missing or empty. + */ + private static void reportMissingAttribute(final Location location, final String elementName, final String attrLocalName) + throws ModelException { + MessageFormat msgFormat = new MessageFormat(ERR_REQUIRED_ATTRIBUTE_MISSING); + String errMsg = msgFormat.format(new Object[] {elementName, attrLocalName, location}); + throw new ModelException(errMsg); + } + + /** * Report an ignored element via the {@link XMLReporter} if available and the class * {@link org.apache.commons.logging.Log}. * http://git-wip-us.apache.org/repos/asf/commons-scxml/blob/144d205d/src/main/java/org/apache/commons/scxml2/io/SCXMLWriter.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/scxml2/io/SCXMLWriter.java b/src/main/java/org/apache/commons/scxml2/io/SCXMLWriter.java index 5f0a8b1..1a12531 100644 --- a/src/main/java/org/apache/commons/scxml2/io/SCXMLWriter.java +++ b/src/main/java/org/apache/commons/scxml2/io/SCXMLWriter.java @@ -871,11 +871,15 @@ public class SCXMLWriter { for (Action a : actions) { if (a instanceof Assign) { Assign asn = (Assign) a; - writer.writeStartElement(XMLNS_SCXML, ELEM_ASSIGN); - writeAV(writer, ATTR_LOCATION, asn.getLocation()); - writeAV(writer, ATTR_SRC, asn.getSrc()); - writeAV(writer, ATTR_EXPR, escapeXML(asn.getExpr())); - writer.writeEndElement(); + if (asn.getNode() != null) { + writeNode(writer, asn.getNode()); + } else { + writer.writeStartElement(XMLNS_SCXML, ELEM_ASSIGN); + writeAV(writer, ATTR_LOCATION, asn.getLocation()); + writeAV(writer, ATTR_SRC, asn.getSrc()); + writeAV(writer, ATTR_EXPR, escapeXML(asn.getExpr())); + writer.writeEndElement(); + } } else if (a instanceof Send) { writeSend(writer, (Send) a); } else if (a instanceof Cancel) { http://git-wip-us.apache.org/repos/asf/commons-scxml/blob/144d205d/src/main/java/org/apache/commons/scxml2/model/Assign.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/scxml2/model/Assign.java b/src/main/java/org/apache/commons/scxml2/model/Assign.java index f2ca819..4658f95 100644 --- a/src/main/java/org/apache/commons/scxml2/model/Assign.java +++ b/src/main/java/org/apache/commons/scxml2/model/Assign.java @@ -16,14 +16,11 @@ */ package org.apache.commons.scxml2.model; -import java.io.IOException; -import org.apache.commons.logging.LogFactory; import org.apache.commons.scxml2.ActionExecutionContext; import org.apache.commons.scxml2.Context; import org.apache.commons.scxml2.Evaluator; -import org.apache.commons.scxml2.PathResolver; import org.apache.commons.scxml2.SCXMLExpressionException; -import org.apache.commons.scxml2.io.ContentParser; +import org.w3c.dom.Node; /** * The class in this SCXML object model that corresponds to the @@ -44,7 +41,7 @@ public class Assign extends Action { private String location; /** - * The source where the new XML instance for this location exists. + * The source location where the new source data for this location exists. */ private String src; @@ -54,6 +51,16 @@ public class Assign extends Action { private String expr; /** + * The assign definition parsed as a standalone DocumentFragment Node (only used by the SCXMLWriter) + */ + private Node node; + + /** + * The parsed value for the child XML data tree or the content of the external src + */ + private Object value; + + /** * Constructor. */ public Assign() { @@ -115,6 +122,46 @@ public class Assign extends Action { } /** + * Get the assign definition parsed as standalone DocumentFragment Node. + * + * @return Node The assign definition parsed as a standalone DocumentFragment <code>Node</code>. + */ + public final Node getNode() { + return node; + } + + /** + * Set the assign definition parsed as standalone DocumentFragment Node. + * + * @param node The child XML data tree, parsed as a standalone DocumentFragment <code>Node</code>. + */ + public final void setNode(final Node node) { + this.node = node; + } + + /** + * Get the parsed value for the child XML data tree or the content of the external src + * @see #setValue(Object) + * @return The parsed value + */ + public final Object getValue() { + return value; + } + + /** + * Sets the parsed value for the child XML data tree or the content of the external src + * @param value a serializable object: + * <ul> + * <li>"Raw" JSON mapped object tree (array->ArrayList, object->LinkedHashMap based)</li> + * <li>XML Node (equals {@link #getNode()})</li> + * <li>space-normalized String</li> + * </ul> + */ + public final void setValue(final Object value) { + this.value = value; + } + + /** * {@inheritDoc} */ @Override @@ -124,11 +171,12 @@ public class Assign extends Action { Evaluator evaluator = exctx.getEvaluator(); ctx.setLocal(getNamespacesKey(), getNamespaces()); Object data; - if (src != null && src.trim().length() > 0) { - data = getSrcData(exctx.getStateMachine().getPathResolver()); - } else { + if (expr != null) { data = evaluator.eval(ctx, expr); } + else { + data = evaluator.cloneData(value); + } evaluator.evalAssign(ctx, location, data); if (exctx.getAppLog().isDebugEnabled()) { @@ -142,31 +190,4 @@ public class Assign extends Action { */ ctx.setLocal(getNamespacesKey(), null); } - - /** - * Get the data the "src" attribute points to. - * - * @return The data the "src" attribute points to. - */ - private Object getSrcData(final PathResolver pathResolver) { - String resolvedSrc = src; - if (pathResolver != null) { - resolvedSrc = pathResolver.resolvePath(src); - } - try { - return ContentParser.DEFAULT_PARSER.parseResource(resolvedSrc); - } catch (IOException e) { - logError(e); - return null; - } - } - - /** - * @param throwable The throwable to log about - */ - private void logError(Throwable throwable) { - org.apache.commons.logging.Log log = LogFactory. - getLog(Assign.class); - log.error(throwable.getMessage(), throwable); - } } http://git-wip-us.apache.org/repos/asf/commons-scxml/blob/144d205d/src/test/java/org/apache/commons/scxml2/w3c/tests.xml ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/scxml2/w3c/tests.xml b/src/test/java/org/apache/commons/scxml2/w3c/tests.xml index 4a64872..f8719cb 100644 --- a/src/test/java/org/apache/commons/scxml2/w3c/tests.xml +++ b/src/test/java/org/apache/commons/scxml2/w3c/tests.xml @@ -57,7 +57,7 @@ <test id="280" mandatory="true" manual="false" jexl="true" ecma="true"/> <test id="550" mandatory="true" manual="false" jexl="true" ecma="true"/> <test id="551" mandatory="true" manual="false" jexl="true" ecma="true"/> - <test id="552" mandatory="true" manual="false" jexl="false" ecma="false"/> + <test id="552" mandatory="true" manual="false" jexl="true" ecma="true"/> <test id="286" mandatory="true" manual="false" jexl="true" ecma="true"/> <test id="287" mandatory="true" manual="false" jexl="true" ecma="true"/> <test id="487" mandatory="true" manual="false" jexl="true" ecma="true"/> @@ -148,7 +148,7 @@ <test id="250" mandatory="true" manual="true" jexl="true" ecma="true" finalState="final"/> <test id="252" mandatory="true" manual="false" jexl="true" ecma="true"/> <test id="253" mandatory="true" manual="false" jexl="true" ecma="true"/> - <test id="530" mandatory="true" manual="false" jexl="false" ecma="false"/> + <test id="530" mandatory="true" manual="false" jexl="true" ecma="true"/> <test id="554" mandatory="true" manual="false" jexl="true" ecma="true"/> <test id="436" mandatory="true" profile="minimal" manual="false" minimal="true"/> <test id="278" mandatory="false" profile="ecma" manual="false" ecma="true"/> @@ -162,7 +162,7 @@ <test id="456" mandatory="false" profile="ecma" manual="false" ecma="true"/> <test id="446" mandatory="false" profile="ecma" manual="false" ecma="false"/> <test id="557" mandatory="false" profile="ecma" manual="false" ecma="false"/> - <test id="558" mandatory="false" profile="ecma" manual="false" ecma="false"/> + <test id="558" mandatory="false" profile="ecma" manual="false" ecma="true"/> <test id="560" mandatory="false" profile="ecma" manual="false" ecma="true"/> <test id="578" mandatory="false" profile="ecma" manual="false" ecma="true"/> <test id="561" mandatory="false" profile="ecma" manual="false" ecma="false"/>
