Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Assign.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Assign.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Assign.java (original) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Assign.java Sat Nov 15 03:07:58 2014 @@ -28,8 +28,6 @@ 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.TriggerEvent; -import org.apache.commons.scxml2.semantics.ErrorConstants; import org.w3c.dom.*; import org.xml.sax.SAXException; @@ -46,12 +44,6 @@ public class Assign extends Action imple private static final long serialVersionUID = 1L; /** - * Left hand side expression evaluating to a previously - * defined variable. - */ - private String name; - - /** * Left hand side expression evaluating to a location within * a previously defined XML data tree. */ @@ -68,33 +60,25 @@ public class Assign extends Action imple private String expr; /** - * {@link PathResolver} for resolving the "src" result. + * Defines the nature of the insertion to be performed, default {@link Evaluator.AssignType#REPLACE_CHILDREN} */ - private PathResolver pathResolver; + private Evaluator.AssignType type; /** - * Constructor. + * The attribute name to add at the specified location when using {@link Evaluator.AssignType#ADD_ATTRIBUTE} */ - public Assign() { - super(); - } + private String attr; /** - * Get the variable to be assigned a new value. - * - * @return Returns the name. + * {@link PathResolver} for resolving the "src" result. */ - public String getName() { - return name; - } + private PathResolver pathResolver; /** - * Get the variable to be assigned a new value. - * - * @param name The name to set. + * Constructor. */ - public void setName(final String name) { - this.name = name; + public Assign() { + super(); } /** @@ -169,6 +153,22 @@ public class Assign extends Action imple this.pathResolver = pathResolver; } + public Evaluator.AssignType getType() { + return type; + } + + public void setType(final Evaluator.AssignType type) { + this.type = type; + } + + public String getAttr() { + return attr; + } + + public void setAttr(final String attr) { + this.attr = attr; + } + /** * {@inheritDoc} */ @@ -176,115 +176,29 @@ public class Assign extends Action imple public void execute(ActionExecutionContext exctx) throws ModelException, SCXMLExpressionException { EnterableState parentState = getParentEnterableState(); Context ctx = exctx.getContext(parentState); - Evaluator eval = exctx.getEvaluator(); + Evaluator evaluator = exctx.getEvaluator(); ctx.setLocal(getNamespacesKey(), getNamespaces()); - // "location" gets preference over "name" - if (location != null) { - Node oldNode = eval.evalLocation(ctx, location); - if (oldNode != null) { - //// rvalue may be ... - // a Node, if so, import it at location - Node newNode; - try { - if (src != null && src.trim().length() > 0) { - newNode = getSrcNode(); - } else { - newNode = eval.evalLocation(ctx, expr); - } - // Remove all children - Node removeChild = oldNode.getFirstChild(); - while (removeChild != null) { - Node nextChild = removeChild.getNextSibling(); - oldNode.removeChild(removeChild); - removeChild = nextChild; - } - if (newNode != null) { - // Adopt new children - for (Node child = newNode.getFirstChild(); - child != null; - child = child.getNextSibling()) { - Node importedNode = oldNode.getOwnerDocument(). - importNode(child, true); - oldNode.appendChild(importedNode); - } - } - } catch (SCXMLExpressionException see) { - // or something else, stuff toString() into lvalue - Object valueObject = eval.eval(ctx, expr); - setNodeValue(oldNode, valueObject.toString()); - } - if (exctx.getAppLog().isDebugEnabled()) { - exctx.getAppLog().debug("<assign>: data node '" + oldNode.getNodeName() - + "' updated"); - } - /* TODO: send to notificationRegistry instead? - TriggerEvent ev = new TriggerEvent(name + ".change", - TriggerEvent.CHANGE_EVENT); - exctx.addInternalEvent(ev); - */ - } else { - exctx.getAppLog().error("<assign>: location does not point to" - + " a <data> node"); - } + Object data; + if (src != null && src.trim().length() > 0) { + data = getSrcNode(); } else { - // lets try "name" (usage as in Sep '05 WD, useful with <var>) - if (!ctx.has(name)) { - exctx.getErrorReporter().onError(ErrorConstants.UNDEFINED_VARIABLE, name - + " = null", parentState); - } else { - Object varObj; - if (src != null && src.trim().length() > 0) { - varObj = getSrcNode(); - } else { - varObj = eval.eval(ctx, expr); - } - ctx.set(name, varObj); - if (exctx.getAppLog().isDebugEnabled()) { - exctx.getAppLog().debug("<assign>: Set variable '" + name + "' to '" - + String.valueOf(varObj) + "'"); - } - TriggerEvent ev = new TriggerEvent(name + ".change", TriggerEvent.CHANGE_EVENT); - exctx.getInternalIOProcessor().addEvent(ev); - } + data = evaluator.eval(ctx, expr); } - ctx.setLocal(getNamespacesKey(), null); - } - /** - * Set node value, depending on its type, from a String. - * - * @param node A Node whose value is to be set - * @param value The new value - */ - private void setNodeValue(final Node node, final String value) { - switch(node.getNodeType()) { - case Node.ATTRIBUTE_NODE: - node.setNodeValue(value); - break; - case Node.ELEMENT_NODE: - //remove all text children - if (node.hasChildNodes()) { - Node child = node.getFirstChild(); - while (child != null) { - if (child.getNodeType() == Node.TEXT_NODE) { - node.removeChild(child); - } - child = child.getNextSibling(); - } - } - //create a new text node and append - Text txt = node.getOwnerDocument().createTextNode(value); - node.appendChild(txt); - break; - case Node.TEXT_NODE: - case Node.CDATA_SECTION_NODE: - ((CharacterData) node).setData(value); - break; - default: - String err = "Trying to set value of a strange Node type: " - + node.getNodeType(); - throw new IllegalArgumentException(err); + evaluator.evalAssign(ctx, location, data, type, attr); + if (exctx.getAppLog().isDebugEnabled()) { + exctx.getAppLog().debug("<assign>: '" + location + "' updated"); + } + // TODO: introduce a optional 'trace.change' setting or something alike to enable .change events, + // but don't do this by default as it can interfere with transitions not expecting such events + /* + if ((Evaluator.XPATH_DATA_MODEL.equals(evaluator.getSupportedDatamodel()) && location.startsWith("$") && ctx.has(location.substring(1)) + || ctx.has(location))) { + TriggerEvent ev = new TriggerEvent(location + ".change", TriggerEvent.CHANGE_EVENT); + exctx.getInternalIOProcessor().addEvent(ev); } + */ + ctx.setLocal(getNamespacesKey(), null); } /** @@ -299,8 +213,7 @@ public class Assign extends Action imple } Document doc = null; try { - doc = DocumentBuilderFactory.newInstance().newDocumentBuilder(). - parse(resolvedSrc); + doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(resolvedSrc); } catch (FactoryConfigurationError t) { logError(t); } catch (SAXException e) { @@ -324,5 +237,4 @@ public class Assign extends Action imple getLog(Assign.class); log.error(throwable.getMessage(), throwable); } - }
Added: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Content.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Content.java?rev=1639829&view=auto ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Content.java (added) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Content.java Sat Nov 15 03:07:58 2014 @@ -0,0 +1,73 @@ +/* + * 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.io.Serializable; + +/** + * The class in this SCXML object model that corresponds to the + * <content> SCXML element. + * + */ +public class Content implements Serializable { + + /** + * Serial version UID. + */ + private static final long serialVersionUID = 1L; + + /** + * The param expression, may be null. + */ + private String expr; + + /** + * The body of this content, may be null. + */ + private Object body; + + /** + * Get the expression for this content. + * + * @return String The expression for this content. + */ + public final String getExpr() { + return expr; + } + + /** + * Set the expression for this content. + * + * @param expr The expression for this content. + */ + public final void setExpr(final String expr) { + this.expr = expr; + } + + /** + * Returns the content body as DocumentFragment + * + * @return the content body as DocumentFragment + */ + public Object getBody() { + return body; + } + + public void setBody(final Object body) { + this.body = body; + } +} Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Content.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Content.java ------------------------------------------------------------------------------ svn:keywords = Id Added: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ContentContainer.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ContentContainer.java?rev=1639829&view=auto ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ContentContainer.java (added) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ContentContainer.java Sat Nov 15 03:07:58 2014 @@ -0,0 +1,38 @@ +/* + * 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; + +/** + * A <code>ContentContainer</code> represents an element in the SCXML + * document that may have a (single) child <content/> element + */ + +public interface ContentContainer { + + /** + * Returns the content + * + * @return the content + */ + Content getContent(); + + /** + * Sets the content + * @param content the content to set + */ + void setContent(Content content); +} Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ContentContainer.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ContentContainer.java ------------------------------------------------------------------------------ svn:keywords = Id Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Invoke.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Invoke.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Invoke.java (original) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Invoke.java Sat Nov 15 03:07:58 2014 @@ -16,21 +16,29 @@ */ package org.apache.commons.scxml2.model; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.HashMap; import java.util.Map; +import org.apache.commons.scxml2.ActionExecutionContext; +import org.apache.commons.scxml2.Context; +import org.apache.commons.scxml2.Evaluator; +import org.apache.commons.scxml2.InvokerManager; import org.apache.commons.scxml2.PathResolver; +import org.apache.commons.scxml2.SCXMLExpressionException; +import org.apache.commons.scxml2.SCXMLIOProcessor; +import org.apache.commons.scxml2.SCXMLSystemContext; +import org.apache.commons.scxml2.TriggerEvent; +import org.apache.commons.scxml2.invoke.Invoker; +import org.apache.commons.scxml2.invoke.InvokerException; +import org.apache.commons.scxml2.semantics.ErrorConstants; +import org.w3c.dom.Node; /** * The class in this SCXML object model that corresponds to the * <invoke> SCXML element. * */ -public class Invoke implements NamespacePrefixesHolder, PathResolverHolder, - Serializable { +public class Invoke extends NamelistHolder implements PathResolverHolder, ContentContainer { /** * Serial version UID. @@ -38,16 +46,36 @@ public class Invoke implements Namespace private static final long serialVersionUID = 1L; /** + * The default context variable key under which the InvokerManager is provided + */ + private static final String INVOKER_MANAGER_KEY = "_INVOKER_MANAGER"; + + /** + * The default target type. + */ + private static final String TYPE_SCXML = "scxml"; + + /** * Identifier for this Invoke. * */ private String id; /** + * Path expression evaluating to a location within a previously defined XML data tree. + */ + private String idlocation; + + /** * The type of target to be invoked. */ private String type; /** + * An expression defining the type of the target to be invoked. + */ + private String typeexpr; + + /** * The source URL for the external service. */ private String src; @@ -64,11 +92,6 @@ public class Invoke implements Namespace private Boolean autoForward; /** - * The List of the params to be sent to the invoked process. - */ - private final List<Param> paramsList; - - /** * The <finalize> child, may be null. */ private Finalize finalize; @@ -79,17 +102,11 @@ public class Invoke implements Namespace private PathResolver pathResolver; /** - * The current XML namespaces in the SCXML document for this action node, - * preserved for deferred XPath evaluation. + * The <content/> of this invoke */ - private Map<String, String> namespaces; + private Content content; - /** - * Default no-args constructor. - */ - public Invoke() { - paramsList = Collections.synchronizedList(new ArrayList<Param>()); - } + private EnterableState parent; /** * Get the identifier for this invoke (may be null). @@ -110,6 +127,21 @@ public class Invoke implements Namespace } /** + * @return the idlocation + */ + public String getIdlocation() { + return idlocation; + } + + /** + * Set the idlocation expression + * @param idlocation The idlocation expression + */ + public void setIdlocation(final String idlocation) { + this.idlocation = idlocation; + } + + /** * Get the type for this <invoke> element. * * @return String Returns the type. @@ -128,6 +160,21 @@ public class Invoke implements Namespace } /** + * @return The type expression + */ + public String getTypeexpr() { + return typeexpr; + } + + /** + * Sets the type expression + * @param typeexpr The type expression to set + */ + public void setTypeexpr(final String typeexpr) { + this.typeexpr = typeexpr; + } + + /** * Get the URL for the external service. * * @return String The URL. @@ -189,24 +236,6 @@ public class Invoke implements Namespace } /** - * Get the list of {@link Param}s. - * - * @return List The params list. - */ - public final List<Param> params() { - return paramsList; - } - - /** - * Add this param to this invoke. - * - * @param param The invoke parameter. - */ - public final void addParam(final Param param) { - paramsList.add(param); - } - - /** * Get the Finalize for this Invoke. * * @return Finalize The Finalize for this Invoke. @@ -243,24 +272,6 @@ public class Invoke implements Namespace } /** - * Get the XML namespaces at this action node in the SCXML document. - * - * @return Returns the map of namespaces. - */ - public final Map<String, String> getNamespaces() { - return namespaces; - } - - /** - * Set the XML namespaces at this action node in the SCXML document. - * - * @param namespaces The document namespaces. - */ - public final void setNamespaces(final Map<String, String> namespaces) { - this.namespaces = namespaces; - } - - /** * Enforce identity equality only * @param other other object to compare with * @return this == other @@ -278,5 +289,139 @@ public class Invoke implements Namespace public final int hashCode() { return System.identityHashCode(this); } -} + /** + * Returns the content + * + * @return the content + */ + public Content getContent() { + return content; + } + + /** + * @return The local context variable name under which the InvokerManager is provided to the Invoke + */ + public String getInvokerManagerKey() { + return INVOKER_MANAGER_KEY; + } + + /** + * Sets the content + * + * @param content the content to set + */ + public void setContent(final Content content) { + this.content = content; + } + + /** + * Get the parent EnterableState. + * + * @return Returns the parent state + */ + public EnterableState getParentEnterableState() { + return parent; + } + + /** + * Set the parent EnterableState. + * @param parent The parent state to set + */ + public void setParentEnterableState(final EnterableState parent) { + if (parent == null) { + throw new IllegalArgumentException("Parent parameter cannot be null"); + } + this.parent = parent; + } + + @SuppressWarnings("unchecked") + @Override + public void execute(final ActionExecutionContext exctx) throws ModelException { + EnterableState parentState = getParentEnterableState(); + Context ctx = exctx.getContext(parentState); + InvokerManager invokerManager = (InvokerManager)ctx.getVars().get(getInvokerManagerKey()); + if (invokerManager == null) { + throw new ModelException("Missing InvokerManager instance in context under key: "+getInvokerManagerKey()); + } + try { + ctx.setLocal(getNamespacesKey(), getNamespaces()); + Evaluator eval = exctx.getEvaluator(); + + String typeValue = type; + if (typeValue == null && typeexpr != null) { + typeValue = (String) getTextContentIfNodeResult(eval.eval(ctx, typeexpr)); + if (typeValue == null) { + throw new SCXMLExpressionException("<invoke> for state "+parentState.getId() + + ": type expression \"" + typeexpr + "\" evaluated to null or empty String"); + } + } + if (typeValue == null) { + typeValue = TYPE_SCXML; + } + Invoker invoker = invokerManager.newInvoker(typeValue); + + String invokeId = getId(); + if (invokeId == null) { + invokeId = parentState.getId() + "." + ctx.get(SCXMLSystemContext.SESSIONID_KEY); + } + if (getId() == null && getIdlocation() != null) { + eval.evalAssign(ctx, idlocation, invokeId, Evaluator.AssignType.REPLACE_CHILDREN, null); + } + invoker.setInvokeId(invokeId); + + String src = getSrc(); + if (src == null && getSrcexpr() != null) { + src = (String) getTextContentIfNodeResult(eval.eval(ctx, getSrcexpr())); + } + if (src != null) { + PathResolver pr = getPathResolver(); + if (pr != null) { + src = getPathResolver().resolvePath(src); + } + } + Node srcNode = null; + if (src == null && getContent() != null) { + Object contentValue; + if (content.getExpr() != null) { + contentValue = eval.eval(ctx, content.getExpr()); + } else { + contentValue = content.getBody(); + } + if (contentValue instanceof Node) { + srcNode = ((Node)contentValue).cloneNode(true); + } + else if (contentValue != null) { + src = String.valueOf(contentValue); + } + } + if (src == null && srcNode == null) { + throw new SCXMLExpressionException("<invoke> for state "+parentState.getId() + + ": no src and no content defined"); + } + Map<String, Object> payloadDataMap = new HashMap<String, Object>(); + addNamelistDataToPayload(exctx, payloadDataMap); + addParamsToPayload(exctx, payloadDataMap); + SCXMLIOProcessor ioProcessor = ((Map<String,SCXMLIOProcessor>)ctx.get(SCXMLSystemContext.IOPROCESSORS_KEY)). + get(SCXMLIOProcessor.SCXML_EVENT_PROCESSOR); + invoker.setParentIOProcessor(ioProcessor); + invoker.setEvaluator(exctx.getEvaluator()); + if (src != null) { + invoker.invoke(src, payloadDataMap); + } + // TODO: } else { invoker.invoke(srcNode, payloadDataMap); } + invokerManager.registerInvoker(this, invoker); + } + catch (InvokerException e) { + exctx.getErrorReporter().onError(ErrorConstants.EXECUTION_ERROR, e.getMessage(), this); + exctx.getInternalIOProcessor().addEvent(new TriggerEvent(TriggerEvent.ERROR_EXECUTION, TriggerEvent.ERROR_EVENT)); + } + catch (SCXMLExpressionException e) { + exctx.getInternalIOProcessor().addEvent(new TriggerEvent(TriggerEvent.ERROR_EXECUTION, TriggerEvent.ERROR_EVENT)); + exctx.getErrorReporter().onError(ErrorConstants.EXPRESSION_ERROR, e.getMessage(), this); + } + finally { + ctx.setLocal(getNamespacesKey(), null); + } + } +} \ No newline at end of file Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Log.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Log.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Log.java (original) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Log.java Sat Nov 15 03:07:58 2014 @@ -95,7 +95,7 @@ public class Log extends Action { Context ctx = exctx.getContext(getParentEnterableState()); Evaluator eval = exctx.getEvaluator(); ctx.setLocal(getNamespacesKey(), getNamespaces()); - exctx.getAppLog().info(label + ": " + String.valueOf(eval.eval(ctx, expr))); + exctx.getAppLog().info(label + ": " + String.valueOf(getTextContentIfNodeResult(eval.eval(ctx, expr)))); ctx.setLocal(getNamespacesKey(), null); } } Added: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/NamelistHolder.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/NamelistHolder.java?rev=1639829&view=auto ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/NamelistHolder.java (added) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/NamelistHolder.java Sat Nov 15 03:07:58 2014 @@ -0,0 +1,97 @@ +/* + * 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.Map; +import java.util.StringTokenizer; + +import org.apache.commons.scxml2.ActionExecutionContext; +import org.apache.commons.scxml2.Context; +import org.apache.commons.scxml2.Evaluator; +import org.apache.commons.scxml2.SCXMLExpressionException; +import org.apache.commons.scxml2.semantics.ErrorConstants; + +/** + * A <code>NamelistHolder</code> represents an element in the SCXML + * document that may have a namelist attribute to + * produce payload for events or external communication. + */ +public abstract class NamelistHolder extends ParamsContainer { + + /** + * The namelist. + */ + private String namelist; + + /** + * Get the namelist. + * + * @return String Returns the namelist. + */ + public final String getNamelist() { + return namelist; + } + + /** + * Set the namelist. + * + * @param namelist The namelist to set. + */ + public final void setNamelist(final String namelist) { + this.namelist = namelist; + } + + /** + * Adds data to the payload data map based on the namelist which names are location expressions + * (typically data ids or for example XPath variables). The names and the values they 'point' at + * are added to the payload data map. + * @param exctx The ActionExecutionContext + * @param payload the payload data map to be updated + * @throws ModelException if this action has not an EnterableState as parent + * @throws SCXMLExpressionException if a malformed or invalid expression is evaluated + * @see PayloadProvider#addToPayload(String, Object, java.util.Map) + */ + protected void addNamelistDataToPayload(ActionExecutionContext exctx, Map<String, Object> payload) + throws ModelException, SCXMLExpressionException { + if (namelist != null) { + EnterableState parentState = getParentEnterableState(); + Context ctx = exctx.getContext(parentState); + try { + ctx.setLocal(getNamespacesKey(), getNamespaces()); + Evaluator evaluator = exctx.getEvaluator(); + StringTokenizer tkn = new StringTokenizer(namelist); + boolean xpathEvaluator = Evaluator.XPATH_DATA_MODEL.equals(evaluator.getSupportedDatamodel()); + while (tkn.hasMoreTokens()) { + String varName = tkn.nextToken(); + Object varObj = evaluator.eval(ctx, varName); + if (varObj == null) { + //considered as a warning here + exctx.getErrorReporter().onError(ErrorConstants.UNDEFINED_VARIABLE, + varName + " = null", parentState); + } + if (xpathEvaluator && varName.startsWith("$")) { + varName = varName.substring(1); + } + addToPayload(varName, varObj, payload); + } + } + finally { + ctx.setLocal(getNamespacesKey(), null); + } + } + } +} Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/NamelistHolder.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/NamelistHolder.java ------------------------------------------------------------------------------ svn:keywords = Id Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Param.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Param.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Param.java (original) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Param.java Sat Nov 15 03:07:58 2014 @@ -37,6 +37,12 @@ public class Param implements NamespaceP private String name; /** + * Left hand side expression evaluating to a location within + * a previously defined XML data tree. + */ + private String location; + + /** * The param expression, may be null. */ private String expr; @@ -73,6 +79,24 @@ public class Param implements NamespaceP } /** + * Get the location for a previously defined XML data tree. + * + * @return Returns the location. + */ + public String getLocation() { + return location; + } + + /** + * Set the location for a previously defined XML data tree. + * + * @param location The location. + */ + public void setLocation(final String location) { + this.location = location; + } + + /** * Get the expression for this param value. * * @return String The expression for this param value. Added: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ParamsContainer.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ParamsContainer.java?rev=1639829&view=auto ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ParamsContainer.java (added) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ParamsContainer.java Sat Nov 15 03:07:58 2014 @@ -0,0 +1,85 @@ +/* + * 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.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.scxml2.ActionExecutionContext; +import org.apache.commons.scxml2.Context; +import org.apache.commons.scxml2.Evaluator; +import org.apache.commons.scxml2.SCXMLExpressionException; + +/** + * A <code>ParamsContainer</code> represents an element in the SCXML + * document that may have one or more <param/> children which are used to + * produce payload for events or external communication. + */ +public abstract class ParamsContainer extends PayloadProvider { + + /** + * The List of the params to be sent + */ + private final List<Param> paramsList = new ArrayList<Param>(); + + /** + * Get the list of {@link Param}s. + * + * @return List The params list. + */ + public List<Param> getParams() { + return paramsList; + } + + /** + * Adds data to the payload data map based on the {@link Param}s of this {@link ParamsContainer} + * @param exctx The ActionExecutionContext + * @param payload the payload data map to be updated + * @throws ModelException if this action has not an EnterableState as parent + * @throws SCXMLExpressionException if a malformed or invalid expression is evaluated + * @see PayloadProvider#addToPayload(String, Object, java.util.Map) + */ + protected void addParamsToPayload(ActionExecutionContext exctx, Map<String, Object> payload) + throws ModelException, SCXMLExpressionException { + if (!paramsList.isEmpty()) { + EnterableState parentState = getParentEnterableState(); + Context ctx = exctx.getContext(parentState); + try { + ctx.setLocal(getNamespacesKey(), getNamespaces()); + Evaluator evaluator = exctx.getEvaluator(); + Object paramValue; + for (Param p : paramsList) { + if (p.getExpr() != null) { + paramValue = evaluator.eval(ctx, p.getExpr()); + } + else if (p.getLocation() != null) { + paramValue = evaluator.eval(ctx, p.getLocation()); + } + else { + // ignore invalid param definition + continue; + } + addToPayload(p.getName(), paramValue, payload); + } + } + finally { + ctx.setLocal(getNamespacesKey(), null); + } + } + } +} Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ParamsContainer.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/ParamsContainer.java ------------------------------------------------------------------------------ svn:keywords = Id Added: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/PayloadProvider.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/PayloadProvider.java?rev=1639829&view=auto ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/PayloadProvider.java (added) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/PayloadProvider.java Sat Nov 15 03:07:58 2014 @@ -0,0 +1,188 @@ +/* + * 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.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.scxml2.Evaluator; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * A <code>PayloadProvider</code> is an element in the SCXML document + * that can provide payload data for an event or an external process. + */ +public abstract class PayloadProvider extends Action { + + /** + * Payload data values wrapper list needed when multiple variable entries use the same names. + * The multiple values are then wrapped in a list. The PayloadBuilder uses this 'marker' list + * to distinguish between entry values which are a list themselves and the wrapper list. + */ + private static class DataValueList extends ArrayList { + } + + /** + * Adds an attribute and value to a payload data map. + * <p> + * As the SCXML specification allows for multiple payload attributes with the same name, this + * method takes care of merging multiple values for the same attribute in a list of values. + * </p> + * <p> + * Furthermore, as modifications of payload data on either the sender or receiver side should affect the + * the other side, attribute values (notably: {@link Node} value only for now) is cloned first before being added + * to the payload data map. This includes 'nested' values within a {@link NodeList}, {@link List} or {@link Map}. + * </p> + * @param attrName the name of the attribute to add + * @param attrValue the value of the attribute to add + * @param payload the payload data map to be updated + */ + @SuppressWarnings("unchecked") + protected void addToPayload(final String attrName, final Object attrValue, Map<String, Object> payload) { + DataValueList valueList = null; + Object value = payload.get(attrName); + if (value != null) { + if (value instanceof DataValueList) { + valueList = (DataValueList)value; + } + else { + valueList = new DataValueList(); + valueList.add(value); + payload.put(attrName, valueList); + } + } + value = clonePayloadValue(attrValue); + if (value instanceof List) { + if (valueList == null) { + valueList = new DataValueList(); + payload.put(attrName, valueList); + } + valueList.addAll((List)value); + } + else if (valueList != null) { + valueList.add(value); + } + else { + payload.put(attrName, value); + } + } + + /** + * Clones a value object for adding to a payload data map. + * <p> + * Currently only clones {@link Node} values. + * </p> + * <p> + * If the value object is an instanceof {@link NodeList}, {@link List} or {@link Map}, its elements + * are also cloned (if possible) through recursive invocation of this same method, and put in + * a new {@link List} or {@link Map} before returning. + * </p> + * @param value the value to be cloned + * @return the cloned value if it could be cloned or otherwise the unmodified value parameter + */ + @SuppressWarnings("unchecked") + protected Object clonePayloadValue(final Object value) { + if (value != null) { + if (value instanceof Node) { + return ((Node)value).cloneNode(true); + } + else if (value instanceof NodeList) { + NodeList nodeList = (NodeList)value; + ArrayList<Node> list = new ArrayList<Node>(); + for (int i = 0, size = nodeList.getLength(); i < size; i++) { + list.add(nodeList.item(i).cloneNode(true)); + } + return list; + } + else if (value instanceof List) { + ArrayList<Object> list = new ArrayList<Object>(); + for (Object v : (List)value) { + list.add(clonePayloadValue(v)); + } + return list; + } + else if (value instanceof Map) { + HashMap<Object, Object> map = new HashMap<Object, Object>(); + for (Map.Entry<Object,Object> entry : ((Map<Object,Object>)value).entrySet()) { + map.put(entry.getKey(), clonePayloadValue(entry.getValue())); + } + return map; + } + // TODO: cloning other type of data? + } + return value; + } + + /** + * Converts a payload data map to be used for an event payload. + * <p> + * Event payload involving key-value pair attributes for an xpath datamodel requires special handling as the + * attributes needs to be contained and put in a "data" element under a 'root' Event payload element. + * </p> + * <p> + * For non-xpath datamodels this method simply returns the original payload parameter unmodified. + * </p> + * @param evaluator the evaluator to test for which datamodel type this event payload is intended + * @param payload the payload data map + * @return payload for an event + * @throws ModelException + */ + protected Object makeEventPayload(final Evaluator evaluator, final Map<String, Object> payload) + throws ModelException { + if (payload != null && !payload.isEmpty() && Evaluator.XPATH_DATA_MODEL.equals(evaluator.getSupportedDatamodel())) { + + try { + Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + Element payloadNode = document.createElement("payload"); + for (Map.Entry<String, Object> entry : payload.entrySet()) { + Element dataNode = document.createElement("data"); + payloadNode.appendChild(dataNode); + dataNode.setAttribute("id", entry.getKey()); + if (entry.getValue() instanceof Node) { + dataNode.appendChild(document.importNode((Node)entry.getValue(), true)); + } + else if (entry.getValue() instanceof DataValueList) { + for (Object value : ((DataValueList)entry.getValue())) { + if (value instanceof Node) { + dataNode.appendChild(document.importNode((Node)entry.getValue(), true)); + } + else { + dataNode.setTextContent(String.valueOf(value)); + } + } + } + else if (entry.getValue() != null) { + dataNode.setTextContent(String.valueOf(entry.getValue())); + } + } + return payloadNode; + } + catch (ParserConfigurationException pce) { + throw new ModelException("Cannot instantiate a DocumentBuilder", pce); + } + } + return payload; + } +} Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/PayloadProvider.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/PayloadProvider.java ------------------------------------------------------------------------------ svn:keywords = Id Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/SCXML.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/SCXML.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/SCXML.java (original) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/SCXML.java Sat Nov 15 03:07:58 2014 @@ -90,9 +90,9 @@ public class SCXML implements Serializab /** - * The datamodel (type) as specified as attribute on this document + * The datamodel name as specified as "datamodel" attribute on this document */ - private String datamodelType; + private String datamodelName; /** * Optional property holding the data model for this SCXML document. @@ -388,19 +388,19 @@ public class SCXML implements Serializab } /** - * Get the datamodel type as specified as attribute on this document - * @return The datamodel type of this document + * Get the datamodel name as specified as attribute on this document + * @return The datamodel name of this document */ - public String getDatamodelType() { - return datamodelType; + public String getDatamodelName() { + return datamodelName; } /** - * Sets the datamodel type as specified as attribute on this document - * @param datamodelType The datamodel type + * Sets the datamodel name as specified as attribute on this document + * @param datamodelName The datamodel name */ - public void setDatamodelType(final String datamodelType) { - this.datamodelType = datamodelType; + public void setDatamodelName(final String datamodelName) { + this.datamodelName = datamodelName; } } Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Send.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Send.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Send.java (original) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/Send.java Sat Nov 15 03:07:58 2014 @@ -16,30 +16,23 @@ */ package org.apache.commons.scxml2.model; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.StringTokenizer; import org.apache.commons.logging.Log; import org.apache.commons.scxml2.ActionExecutionContext; import org.apache.commons.scxml2.Context; import org.apache.commons.scxml2.Evaluator; import org.apache.commons.scxml2.SCXMLExpressionException; +import org.apache.commons.scxml2.SCXMLIOProcessor; import org.apache.commons.scxml2.SCXMLSystemContext; -import org.apache.commons.scxml2.TriggerEvent; -import org.apache.commons.scxml2.semantics.ErrorConstants; -import org.w3c.dom.Node; -import org.w3c.dom.Text; -import org.w3c.dom.CharacterData; /** * The class in this SCXML object model that corresponds to the * <send> SCXML element. * */ -public class Send extends Action implements ExternalContent { +public class Send extends NamelistHolder implements ContentContainer { /** * Serial version UID. @@ -52,25 +45,28 @@ public class Send extends Action impleme private static final String TYPE_SCXML = "scxml"; /** - * The spec mandated derived event when target cannot be reached - * for TYPE_SCXML. + * The suffix in the delay string for milliseconds. */ - private static final String EVENT_ERR_SEND_TARGETUNAVAILABLE = - "error.send.targetunavailable"; - - /** The suffix in the delay string for milliseconds. */ private static final String MILLIS = "ms"; - /** The suffix in the delay string for seconds. */ + /** + * The suffix in the delay string for seconds. + */ private static final String SECONDS = "s"; - /** The suffix in the delay string for minutes. */ + /** + * The suffix in the delay string for minutes. + */ private static final String MINUTES = "m"; - /** The number of milliseconds in a second. */ + /** + * The number of milliseconds in a second. + */ private static final long MILLIS_IN_A_SECOND = 1000L; - /** The number of milliseconds in a minute. */ + /** + * The number of milliseconds in a minute. + */ private static final long MILLIS_IN_A_MINUTE = 60000L; /** @@ -115,22 +111,11 @@ public class Send extends Action impleme private String delayexpr; /** - * The data containing information which may be used by the - * implementing platform to configure the event processor. + * The data containing information which may be used by the implementing platform to configure the event processor. */ private String hints; /** - * The namelist to the sent. - */ - private String namelist; - - /** - * The list of external nodes associated with this <send> element. - */ - private List<Node> externalNodes; - - /** * The type of event being generated. */ private String event; @@ -141,11 +126,15 @@ public class Send extends Action impleme private String eventexpr; /** + * The <content/> of this send + */ + private Content content; + + /** * Constructor. */ public Send() { super(); - this.externalNodes = new ArrayList<Node>(); } /** @@ -157,6 +146,7 @@ public class Send extends Action impleme /** * Set the idlocation expression + * * @param idlocation The idlocation expression */ public void setIdlocation(final String idlocation) { @@ -190,6 +180,7 @@ public class Send extends Action impleme /** * Set the delay expression + * * @param delayexpr The delay expression to set */ public void setDelayexpr(final String delayexpr) { @@ -197,15 +188,6 @@ public class Send extends Action impleme } /** - * Get the list of external namespaced child nodes. - * - * @return List Returns the list of externalnodes. - */ - public final List<Node> getExternalNodes() { - return externalNodes; - } - - /** * Get the hints for this <send> element. * * @return String Returns the hints. @@ -224,24 +206,6 @@ public class Send extends Action impleme } /** - * Get the namelist. - * - * @return String Returns the namelist. - */ - public final String getNamelist() { - return namelist; - } - - /** - * Set the namelist. - * - * @param namelist The namelist to set. - */ - public final void setNamelist(final String namelist) { - this.namelist = namelist; - } - - /** * Get the identifier for this <send> element. * * @return String Returns the id. @@ -286,6 +250,7 @@ public class Send extends Action impleme /** * Set the target expression + * * @param targetexpr The target expression to set */ public void setTargetexpr(final String targetexpr) { @@ -319,6 +284,7 @@ public class Send extends Action impleme /** * Sets the type expression + * * @param typeexpr The type expression to set */ public void setTypeexpr(final String typeexpr) { @@ -352,6 +318,7 @@ public class Send extends Action impleme /** * Sets the event expression + * * @param eventexpr The event expression to set */ public void setEventexpr(final String eventexpr) { @@ -359,8 +326,27 @@ public class Send extends Action impleme } /** + * Returns the content + * + * @return the content + */ + public Content getContent() { + return content; + } + + /** + * Sets the content + * + * @param content the content to set + */ + public void setContent(final Content content) { + this.content = content; + } + + /** * {@inheritDoc} */ + @SuppressWarnings("unchecked") @Override public void execute(ActionExecutionContext exctx) throws ModelException, SCXMLExpressionException { // Send attributes evaluation @@ -375,61 +361,53 @@ public class Send extends Action impleme hintsValue = eval.eval(ctx, hints); } if (id == null) { - id = ((SCXMLSystemContext)exctx.getGlobalContext().getParent()).generateSessionId(); + id = ctx.getSystemContext().generateSessionId(); if (idlocation != null) { - Node location = eval.evalLocation(ctx, idlocation); - if (location != null) { - setNodeValue(location, id); - } - else { - throw new ModelException("<send>: idlocation does not point to a <data> node"); - } + eval.evalAssign(ctx, idlocation, id, Evaluator.AssignType.REPLACE_CHILDREN, null); } } String targetValue = target; if (targetValue == null && targetexpr != null) { - targetValue = (String) eval.eval(ctx, targetexpr); + targetValue = (String) getTextContentIfNodeResult(eval.eval(ctx, targetexpr)); if ((targetValue == null || targetValue.trim().length() == 0) && exctx.getAppLog().isWarnEnabled()) { exctx.getAppLog().warn("<send>: target expression \"" + targetexpr - + "\" evaluated to null or empty String"); + + "\" evaluated to null or empty String"); } } String typeValue = type; if (typeValue == null && typeexpr != null) { - typeValue = (String) eval.eval(ctx, typeexpr); + typeValue = (String) getTextContentIfNodeResult(eval.eval(ctx, typeexpr)); if ((typeValue == null || typeValue.trim().length() == 0) && exctx.getAppLog().isWarnEnabled()) { exctx.getAppLog().warn("<send>: type expression \"" + typeexpr - + "\" evaluated to null or empty String"); + + "\" evaluated to null or empty String"); } } if (typeValue == null) { // must default to 'scxml' when unspecified typeValue = TYPE_SCXML; - } - else if (!TYPE_SCXML.equals(typeValue) && typeValue.trim().equalsIgnoreCase(TYPE_SCXML)) { + } else if (!TYPE_SCXML.equals(typeValue) && typeValue.trim().equalsIgnoreCase(TYPE_SCXML)) { typeValue = TYPE_SCXML; } - Map<String, Object> params = null; - if (namelist != null) { - StringTokenizer tkn = new StringTokenizer(namelist); - params = new HashMap<String, Object>(tkn.countTokens()); - while (tkn.hasMoreTokens()) { - String varName = tkn.nextToken(); - Object varObj = ctx.get(varName); - if (varObj == null) { - //considered as a warning here - exctx.getErrorReporter().onError(ErrorConstants.UNDEFINED_VARIABLE, - varName + " = null", parentState); - } - params.put(varName, varObj); + Object payload = null; + Map<String, Object> payloadDataMap = new HashMap<String, Object>(); + addNamelistDataToPayload(exctx, payloadDataMap); + addParamsToPayload(exctx, payloadDataMap); + if (!payloadDataMap.isEmpty()) { + payload = makeEventPayload(eval, payloadDataMap); + } + else if (content != null) { + if (content.getExpr() != null) { + payload = clonePayloadValue(eval.eval(ctx, content.getExpr())); + } else { + payload = clonePayloadValue(content.getBody()); } } long wait = 0L; String delayString = delay; if (delayString == null && delayexpr != null) { - Object delayValue = eval.eval(ctx, delay); + Object delayValue = getTextContentIfNodeResult(eval.eval(ctx, delayexpr)); if (delayValue != null) { delayString = delayValue.toString(); } @@ -439,63 +417,34 @@ public class Send extends Action impleme } String eventValue = event; if (eventValue == null && eventexpr != null) { - eventValue = (String) eval.eval(ctx, eventexpr); + eventValue = (String) getTextContentIfNodeResult(eval.eval(ctx, eventexpr)); if ((eventValue == null || eventValue.trim().length() == 0) && exctx.getAppLog().isWarnEnabled()) { throw new SCXMLExpressionException("<send>: event expression \"" + eventexpr + "\" evaluated to null or empty String"); } } - // Lets see if we should handle it ourselves - if (typeValue != null && TYPE_SCXML.equals(typeValue)) { - if (eventValue == null) { - // event required when type == http://www.w3.org/TR/scxml/#SCXMLEventProcessor - throw new ModelException("Event parameter is required for <send> with type=\"scxml\""); - } - if (targetValue == null || targetValue.trim().length() == 0) { - // TODO: Remove both short-circuit passes in v1.0 - if (wait == 0L) { - if (exctx.getAppLog().isDebugEnabled()) { - exctx.getAppLog().debug("<send>: Enqueued event '" + eventValue - + "' with no delay"); - } - exctx.getInternalIOProcessor().addEvent( - new TriggerEvent(eventValue, TriggerEvent.SIGNAL_EVENT, params)); - return; - } - } else { - // We know of no other - if (exctx.getAppLog().isWarnEnabled()) { - exctx.getAppLog().warn("<send>: Unavailable target - " - + targetValue); - } - exctx.getInternalIOProcessor().addEvent( - new TriggerEvent(EVENT_ERR_SEND_TARGETUNAVAILABLE, TriggerEvent.ERROR_EVENT)); - // short-circuit the EventDispatcher - return; - } - } + Map<String, SCXMLIOProcessor> ioProcessors = (Map<String, SCXMLIOProcessor>) ctx.get(SCXMLSystemContext.IOPROCESSORS_KEY); ctx.setLocal(getNamespacesKey(), null); if (exctx.getAppLog().isDebugEnabled()) { exctx.getAppLog().debug("<send>: Dispatching event '" + eventValue - + "' to target '" + targetValue + "' of target type '" - + typeValue + "' with suggested delay of " + wait - + "ms"); - } - // Else, let the EventDispatcher take care of it - exctx.getEventDispatcher().send(id, targetValue, typeValue, eventValue, - params, hintsValue, wait, externalNodes); + + "' to target '" + targetValue + "' of target type '" + + typeValue + "' with suggested delay of " + wait + + "ms"); + } + exctx.getEventDispatcher().send(ioProcessors, id, targetValue, typeValue, eventValue, + payload, hintsValue, wait); } /** * Parse delay. * * @param delayString The String value of the delay, in CSS2 format - * @param appLog The application log + * @param appLog The application log * @return The parsed delay in milliseconds * @throws SCXMLExpressionException If the delay cannot be parsed */ private long parseDelay(final String delayString, final Log appLog) - throws SCXMLExpressionException { + throws SCXMLExpressionException { long wait = 0L; long multiplier = 1L; @@ -525,42 +474,5 @@ public class Send extends Action impleme } return wait; } - - /** - * Set node value, depending on its type, from a String. - * - * @param node A Node whose value is to be set - * @param value The new value - */ - private void setNodeValue(final Node node, final String value) { - switch(node.getNodeType()) { - case Node.ATTRIBUTE_NODE: - node.setNodeValue(value); - break; - case Node.ELEMENT_NODE: - //remove all text children - if (node.hasChildNodes()) { - Node child = node.getFirstChild(); - while (child != null) { - if (child.getNodeType() == Node.TEXT_NODE) { - node.removeChild(child); - } - child = child.getNextSibling(); - } - } - //create a new text node and append - Text txt = node.getOwnerDocument().createTextNode(value); - node.appendChild(txt); - break; - case Node.TEXT_NODE: - case Node.CDATA_SECTION_NODE: - ((CharacterData) node).setData(value); - break; - default: - String err = "Trying to set value of a strange Node type: " - + node.getNodeType(); - throw new IllegalArgumentException(err); - } - } } Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/TransitionalState.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/TransitionalState.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/TransitionalState.java (original) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/model/TransitionalState.java Sat Nov 15 03:07:58 2014 @@ -217,6 +217,7 @@ public abstract class TransitionalState */ public final void addInvoke(final Invoke invoke) { this.invokes.add(invoke); + invoke.setParentEnterableState(this); } /** Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/semantics/ErrorConstants.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/semantics/ErrorConstants.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/semantics/ErrorConstants.java (original) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/semantics/ErrorConstants.java Sat Nov 15 03:07:58 2014 @@ -60,6 +60,11 @@ public class ErrorConstants { */ public static final String EXPRESSION_ERROR = "EXPRESSION_ERROR"; + /** + * An execution error. + */ + public static final String EXECUTION_ERROR = "EXECUTION_ERROR"; + //---------------------------------------------- STATIC CONSTANTS ONLY /** Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/semantics/SCXMLSemanticsImpl.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/semantics/SCXMLSemanticsImpl.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/semantics/SCXMLSemanticsImpl.java (original) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/semantics/SCXMLSemanticsImpl.java Sat Nov 15 03:07:58 2014 @@ -25,10 +25,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.commons.scxml2.ActionExecutionContext; import org.apache.commons.scxml2.Context; import org.apache.commons.scxml2.ErrorReporter; -import org.apache.commons.scxml2.Evaluator; -import org.apache.commons.scxml2.PathResolver; import org.apache.commons.scxml2.SCInstance; import org.apache.commons.scxml2.SCXMLExecutionContext; import org.apache.commons.scxml2.SCXMLExpressionException; @@ -47,7 +46,6 @@ import org.apache.commons.scxml2.model.H import org.apache.commons.scxml2.model.Invoke; import org.apache.commons.scxml2.model.OnEntry; import org.apache.commons.scxml2.model.OnExit; -import org.apache.commons.scxml2.model.Param; import org.apache.commons.scxml2.model.Script; import org.apache.commons.scxml2.model.SimpleTransition; import org.apache.commons.scxml2.model.TransitionalState; @@ -1074,82 +1072,15 @@ public class SCXMLSemanticsImpl implemen * @param statesToInvoke the set of activated states which invokes need to be invoked */ public void initiateInvokes(final SCXMLExecutionContext exctx, - final Set<TransitionalState> statesToInvoke) { - SCInstance scInstance = exctx.getScInstance(); - Evaluator eval = exctx.getEvaluator(); + final Set<TransitionalState> statesToInvoke) throws ModelException { + ActionExecutionContext aexctx = exctx.getActionExecutionContext(); for (TransitionalState ts : statesToInvoke) { - if (ts.getInvokes().isEmpty()) { - continue; - } - Context context = scInstance.getContext(ts); - for (Invoke i : ts.getInvokes()) { - String src = i.getSrc(); - if (src == null) { - String srcexpr = i.getSrcexpr(); - Object srcObj; - try { - context.setLocal(Context.NAMESPACES_KEY, i.getNamespaces()); - srcObj = eval.eval(context, srcexpr); - context.setLocal(Context.NAMESPACES_KEY, null); - src = String.valueOf(srcObj); - } catch (SCXMLExpressionException see) { - exctx.getInternalIOProcessor().addEvent(new TriggerEvent(TriggerEvent.ERROR_EXECUTION, TriggerEvent.ERROR_EVENT)); - exctx.getErrorReporter().onError(ErrorConstants.EXPRESSION_ERROR, see.getMessage(), i); - } - } - String source = src; - PathResolver pr = i.getPathResolver(); - if (pr != null) { - source = i.getPathResolver().resolvePath(src); - } - Invoker inv; - try { - inv = exctx.newInvoker(i.getType()); - } catch (InvokerException ie) { - exctx.getInternalIOProcessor().addEvent(new TriggerEvent("failed.invoke."+ts.getId(), TriggerEvent.ERROR_EVENT)); - continue; - } - List<Param> params = i.params(); - Map<String, Object> args = new HashMap<String, Object>(); - for (Param p : params) { - String argExpr = p.getExpr(); - Object argValue = null; - context.setLocal(Context.NAMESPACES_KEY, p.getNamespaces()); - // Do we have an "expr" attribute? - if (argExpr != null && argExpr.trim().length() > 0) { - try { - argValue = eval.eval(context, argExpr); - } catch (SCXMLExpressionException see) { - exctx.getInternalIOProcessor().addEvent(new TriggerEvent(TriggerEvent.ERROR_EXECUTION, TriggerEvent.ERROR_EVENT)); - exctx.getErrorReporter().onError(ErrorConstants.EXPRESSION_ERROR, see.getMessage(), i); - } - } else { - // No. Does value of "name" attribute refer to a valid - // location in the data model? - try { - argValue = eval.evalLocation(context, p.getName()); - if (argValue == null) { - // Generate error, 4.3.1 in WD-scxml-20080516 - exctx.getInternalIOProcessor().addEvent(new TriggerEvent(ts.getId() + ERR_ILLEGAL_ALLOC, TriggerEvent.ERROR_EVENT)); - } - } catch (SCXMLExpressionException see) { - exctx.getInternalIOProcessor().addEvent(new TriggerEvent(TriggerEvent.ERROR_EXECUTION, TriggerEvent.ERROR_EVENT)); - exctx.getErrorReporter().onError(ErrorConstants.EXPRESSION_ERROR, see.getMessage(), i); - } - } - context.setLocal(Context.NAMESPACES_KEY, null); - args.put(p.getName(), argValue); - } - String invokeId = exctx.setInvoker(i, inv); - inv.setInvokeId(invokeId); - inv.setParentIOProcessor(exctx.getExternalIOProcessor()); - inv.setEvaluator(exctx.getEvaluator()); - try { - inv.invoke(source, args); - } catch (InvokerException ie) { - exctx.getInternalIOProcessor().addEvent(new TriggerEvent("failed.invoke."+ts.getId(), TriggerEvent.ERROR_EVENT)); - exctx.removeInvoker(i); - } + for (Invoke invoke : ts.getInvokes()) { + Context ctx = aexctx.getContext(invoke.getParentEnterableState()); + String invokerManagerKey = invoke.getInvokerManagerKey(); + ctx.setLocal(invokerManagerKey, exctx); + invoke.execute(aexctx); + ctx.setLocal(invokerManagerKey, null); } } } Modified: commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/test/StandaloneUtils.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/test/StandaloneUtils.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/test/StandaloneUtils.java (original) +++ commons/proper/scxml/trunk/src/main/java/org/apache/commons/scxml2/test/StandaloneUtils.java Sat Nov 15 03:07:58 2014 @@ -27,10 +27,8 @@ import javax.xml.stream.XMLStreamExcepti import org.apache.commons.scxml2.Context; import org.apache.commons.scxml2.Evaluator; -import org.apache.commons.scxml2.EventDispatcher; import org.apache.commons.scxml2.SCXMLExecutor; import org.apache.commons.scxml2.TriggerEvent; -import org.apache.commons.scxml2.env.SimpleScheduler; import org.apache.commons.scxml2.env.Tracer; import org.apache.commons.scxml2.invoke.SimpleSCXMLInvoker; import org.apache.commons.scxml2.io.SCXMLReader; @@ -82,8 +80,6 @@ public final class StandaloneUtils { } System.out.println(SCXMLWriter.write(doc)); SCXMLExecutor exec = new SCXMLExecutor(evaluator, null, trc); - EventDispatcher ed = new SimpleScheduler(exec); - exec.setEventdispatcher(ed); exec.setStateMachine(doc); exec.addListener(doc, trc); exec.registerInvokerClass("scxml", SimpleSCXMLInvoker.class); @@ -113,7 +109,7 @@ public final class StandaloneUtils { rootCtx.setLocal(name, value); System.out.println("Set variable " + name + " to " + value); - } else if (event == null || event.trim().length() == 0 + } else if (event.trim().length() == 0 || event.equalsIgnoreCase("null")) { TriggerEvent[] evts = {new TriggerEvent(null, TriggerEvent.SIGNAL_EVENT, null)}; Modified: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/EventDataTest.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/EventDataTest.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/EventDataTest.java (original) +++ commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/EventDataTest.java Sat Nov 15 03:07:58 2014 @@ -18,7 +18,6 @@ package org.apache.commons.scxml2; import java.util.Set; -import org.apache.commons.scxml2.env.SimpleScheduler; import org.apache.commons.scxml2.env.Tracer; import org.apache.commons.scxml2.model.EnterableState; import org.apache.commons.scxml2.model.SCXML; @@ -97,7 +96,6 @@ public class EventDataTest { SCXML scxml = SCXMLTestHelper.parse("org/apache/commons/scxml2/env/jexl/eventdata-03.xml"); Tracer trc = new Tracer(); SCXMLExecutor exec = new SCXMLExecutor(null, null, trc); - exec.setEventdispatcher(new SimpleScheduler(exec)); exec.addListener(scxml, trc); exec.setStateMachine(scxml); exec.go(); Modified: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/WizardsTest.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/WizardsTest.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/WizardsTest.java (original) +++ commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/WizardsTest.java Sat Nov 15 03:07:58 2014 @@ -16,18 +16,17 @@ */ package org.apache.commons.scxml2; -import java.io.Serializable; -import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.commons.scxml2.env.SimpleDispatcher; import org.apache.commons.scxml2.model.EnterableState; import org.junit.Assert; import org.junit.Test; -import org.w3c.dom.Node; + /** - * Unit tests {@link org.apache.commons.scxml2.SCXMLExecutor}. + * Unit tests */ public class WizardsTest { @@ -74,25 +73,33 @@ public class WizardsTest { Assert.assertEquals("state4", currentStates.iterator().next().getId()); } - static class TestEventDispatcher implements EventDispatcher, Serializable { + static class TestEventDispatcher extends SimpleDispatcher { private static final long serialVersionUID = 1L; // If you change this, you must also change testWizard02Sample() + int callback = 0; - public void send(String id, String target, String type, - String event, Map<String, Object> params, Object hints, long delay, - List<Node> externalNodes) { - int i = ((Integer) params.get("aValue")).intValue(); - switch (callback) { - case 0: - Assert.assertTrue(i == 2); // state2 - callback++; - break; - case 1: - Assert.assertTrue(i == 4); // state4 - callback++; - break; - default: - Assert.fail("More than 2 TestEventDispatcher <send> callbacks"); + + @SuppressWarnings("unchecked") + public void send(Map<String, SCXMLIOProcessor> ioProcessors, String id, String target, String type, + String event, Object data, Object hints, long delay) { + if ("foo".equals(type)) { + Map<String, Object> params = (Map<String, Object>)data; + int i = ((Integer) params.get("aValue")); + switch (callback) { + case 0: + Assert.assertTrue(i == 2); // state2 + callback++; + break; + case 1: + Assert.assertTrue(i == 4); // state4 + callback++; + break; + default: + Assert.fail("More than 2 TestEventDispatcher <send> callbacks for type \"foo\""); + } + } + else { + super.send(ioProcessors, id, target, type, event, data, hints, delay); } } public void cancel(String sendId) { Modified: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/custom-hello-world-04-jexl.xml URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/custom-hello-world-04-jexl.xml?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/custom-hello-world-04-jexl.xml (original) +++ commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/custom-hello-world-04-jexl.xml Sat Nov 15 03:07:58 2014 @@ -29,7 +29,7 @@ </onentry> <transition event="helloevent" target="middle1"> - <assign name="helloName1" expr="_event.data" /> + <assign location="helloName1" expr="_event.data" /> </transition> </state> @@ -47,7 +47,7 @@ </onentry> <transition event="helloevent" target="custom3"> - <assign name="helloName1" expr="_event.data" /> + <assign location="helloName1" expr="_event.data" /> </transition> </state> Modified: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/SimpleContextTest.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/SimpleContextTest.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/SimpleContextTest.java (original) +++ commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/SimpleContextTest.java Sat Nov 15 03:07:58 2014 @@ -57,7 +57,7 @@ public class SimpleContextTest { Map<String, Object> parentVars = new HashMap<String, Object>(); parentVars.put("key", "value"); - SimpleContext parentContext = new SimpleContext(parentVars); + SimpleContext parentContext = new SimpleContext(null, parentVars); Map<String, Object> vars = new HashMap<String, Object>(); vars.put("key", "value"); @@ -73,7 +73,7 @@ public class SimpleContextTest { Map<String, Object> parentVars = new HashMap<String, Object>(); parentVars.put("differentKey", "value"); - SimpleContext parentContext = new SimpleContext(parentVars); + SimpleContext parentContext = new SimpleContext(null, parentVars); Map<String, Object> vars = new HashMap<String, Object>(); vars.put("key", "value"); @@ -106,7 +106,7 @@ public class SimpleContextTest { Map<String, Object> parentVars = new HashMap<String, Object>(); parentVars.put("differentKey", "differentValue"); - SimpleContext parentContext = new SimpleContext(parentVars); + SimpleContext parentContext = new SimpleContext(null, parentVars); Map<String, Object> vars = new HashMap<String, Object>(); vars.put("key", "value"); @@ -132,7 +132,7 @@ public class SimpleContextTest { Map<String, Object> parentVars = new HashMap<String, Object>(); parentVars.put("differentKey", "differentValue"); - SimpleContext parentContext = new SimpleContext(parentVars); + SimpleContext parentContext = new SimpleContext(null, parentVars); Map<String, Object> vars = new HashMap<String, Object>(); vars.put("key", "value"); @@ -170,7 +170,7 @@ public class SimpleContextTest { Map<String, Object> parentVars = new HashMap<String, Object>(); parentVars.put("differentKey", "differentValue"); - SimpleContext parentContext = new SimpleContext(parentVars); + SimpleContext parentContext = new SimpleContext(null, parentVars); Map<String, Object> vars = new HashMap<String, Object>(); vars.put("key", "value"); Modified: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/GroovyContextTest.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/GroovyContextTest.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/GroovyContextTest.java (original) +++ commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/GroovyContextTest.java Sat Nov 15 03:07:58 2014 @@ -34,7 +34,7 @@ public class GroovyContextTest { public void testPrepopulated() { Map<String, Object> m = new HashMap<String, Object>(); m.put("foo", "bar"); - GroovyContext ctx = new GroovyContext(m, null); + GroovyContext ctx = new GroovyContext(null, m, null); Assert.assertNotNull(ctx); Assert.assertEquals(1, ctx.getVars().size()); String fooValue = (String) ctx.get("foo"); Modified: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/GroovyEvaluatorTest.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/GroovyEvaluatorTest.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/GroovyEvaluatorTest.java (original) +++ commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/GroovyEvaluatorTest.java Sat Nov 15 03:07:58 2014 @@ -33,10 +33,12 @@ public class GroovyEvaluatorTest { private String BAD_EXPRESSION = ">"; private Context ctx; + private Context rootCtx; @Before public void before() { - ctx = new GroovyContext(); + rootCtx = new GroovyContext(); + ctx = new GroovyContext(new SCXMLSystemContext(rootCtx), null); } @Test @@ -60,7 +62,7 @@ public class GroovyEvaluatorTest { state1.setId("state1"); allStates.add(state1); - ctx.setLocal(SCXMLSystemContext.ALL_STATES_KEY, allStates); + rootCtx.set(SCXMLSystemContext.ALL_STATES_KEY, allStates); Assert.assertTrue(eval.evalCond(ctx, "In('state1')")); } Modified: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/StaticMethodTest.java URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/StaticMethodTest.java?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/StaticMethodTest.java (original) +++ commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/StaticMethodTest.java Sat Nov 15 03:07:58 2014 @@ -35,23 +35,4 @@ public class StaticMethodTest { Assert.assertEquals(1, currentStates.size()); Assert.assertEquals("static", currentStates.iterator().next().getId()); } - - @Test - public void mytest() throws Exception { - Object o1, o2 = new Object(); - o1 = new Object(); - - o2 = null; - o1 = null; - if (val(o1,o2)) { - System.out.println("hello"); - } - else { - System.out.println("boo"); - } - } - - public boolean val(Object o1, Object o2) { - return (o1 == o2); - } } Modified: commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/groovy-closure.xml URL: http://svn.apache.org/viewvc/commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/groovy-closure.xml?rev=1639829&r1=1639828&r2=1639829&view=diff ============================================================================== --- commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/groovy-closure.xml (original) +++ commons/proper/scxml/trunk/src/test/java/org/apache/commons/scxml2/env/groovy/groovy-closure.xml Sat Nov 15 03:07:58 2014 @@ -85,7 +85,7 @@ <transition cond="!In('closed')" target="idle"/> <transition event="time" target="cooking"> - <assign name="timer" expr="timer + 1"/> + <assign location="timer" expr="timer + 1"/> </transition> </state> </state>
