summaryrefslogtreecommitdiffstats
path: root/src/uscxml/interpreter
diff options
context:
space:
mode:
authorStefan Radomski <github@mintwerk.de>2016-05-12 13:12:33 (GMT)
committerStefan Radomski <github@mintwerk.de>2016-05-12 13:12:33 (GMT)
commitb62e7979600feee23dc7cdb61042a8fc7673122b (patch)
treef7351372f37979dd2d048e0b68a16a4cd3b2aadb /src/uscxml/interpreter
parent1b11b310be61e51b3ac5ebb83f7c8a33aef3d6e8 (diff)
downloaduscxml-b62e7979600feee23dc7cdb61042a8fc7673122b.zip
uscxml-b62e7979600feee23dc7cdb61042a8fc7673122b.tar.gz
uscxml-b62e7979600feee23dc7cdb61042a8fc7673122b.tar.bz2
Major Refactoring v2.0
Diffstat (limited to 'src/uscxml/interpreter')
-rw-r--r--src/uscxml/interpreter/ContentExecutorImpl.cpp649
-rw-r--r--src/uscxml/interpreter/ContentExecutorImpl.h143
-rw-r--r--src/uscxml/interpreter/EventQueueImpl.cpp189
-rw-r--r--src/uscxml/interpreter/EventQueueImpl.h125
-rw-r--r--src/uscxml/interpreter/InterpreterDraft6.cpp573
-rw-r--r--src/uscxml/interpreter/InterpreterDraft6.h56
-rw-r--r--src/uscxml/interpreter/InterpreterFast.cpp42
-rw-r--r--src/uscxml/interpreter/InterpreterFast.h47
-rw-r--r--src/uscxml/interpreter/InterpreterImpl.cpp361
-rw-r--r--src/uscxml/interpreter/InterpreterImpl.h290
-rw-r--r--src/uscxml/interpreter/InterpreterMonitor.h95
-rw-r--r--src/uscxml/interpreter/InterpreterRC.cpp661
-rw-r--r--src/uscxml/interpreter/InterpreterRC.h68
-rw-r--r--src/uscxml/interpreter/MicroStepFast.cpp1149
-rw-r--r--src/uscxml/interpreter/MicroStepFast.h127
-rw-r--r--src/uscxml/interpreter/MicroStepImpl.h127
16 files changed, 3255 insertions, 1447 deletions
diff --git a/src/uscxml/interpreter/ContentExecutorImpl.cpp b/src/uscxml/interpreter/ContentExecutorImpl.cpp
new file mode 100644
index 0000000..6a06bec
--- /dev/null
+++ b/src/uscxml/interpreter/ContentExecutorImpl.cpp
@@ -0,0 +1,649 @@
+/**
+ * @file
+ * @author 2016 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#include "ContentExecutorImpl.h"
+#include "uscxml/util/String.h"
+#include "uscxml/util/Predicates.h"
+#include "uscxml/util/UUID.h"
+#include "uscxml/util/URL.h"
+#include "uscxml/messages/Data.h"
+
+#include <xercesc/parsers/XercesDOMParser.hpp>
+#include <xercesc/sax/HandlerBase.hpp>
+#include <xercesc/framework/MemBufInputSource.hpp>
+
+#include "easylogging++.h"
+
+namespace uscxml {
+
+using namespace xercesc;
+
+void BasicContentExecutorImpl::processRaise(xercesc::DOMElement* content) {
+ Event raised(ATTR(content, "event"));
+ _callbacks->enqueueInternal(raised);
+}
+
+void BasicContentExecutorImpl::processSend(xercesc::DOMElement* element) {
+ Event sendEvent;
+ std::string target;
+ std::string type = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor"; // default
+ uint32_t delayMs = 0;
+
+ // test 331
+ sendEvent.eventType = Event::EXTERNAL;
+
+ // test 228
+ std::string invokeId = _callbacks->getInvokeId();
+ if (invokeId.size() > 0) {
+ sendEvent.invokeid = invokeId;
+ }
+
+ try {
+ // event
+ if (HAS_ATTR(element, "eventexpr")) {
+ sendEvent.name = _callbacks->evalAsData(ATTR(element, "eventexpr")).atom;
+ } else if (HAS_ATTR(element, "event")) {
+ sendEvent.name = ATTR(element, "event");
+ }
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in send element eventexpr", element);
+ }
+
+ try {
+ // target
+ if (HAS_ATTR(element, "targetexpr")) {
+ target = _callbacks->evalAsData(ATTR(element, "targetexpr")).atom;
+ } else if (HAS_ATTR(element, "target")) {
+ target = ATTR(element, "target");
+ }
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in send element targetexpr", element);
+ }
+
+ try {
+ // type
+ if (HAS_ATTR(element, "typeexpr")) {
+ type = _callbacks->evalAsData(ATTR(element, "typeexpr")).atom;
+ } else if (HAS_ATTR(element, "type")) {
+ type = ATTR(element, "type");
+ }
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in send element typeexpr", element);
+ }
+
+ try {
+ // id
+ if (HAS_ATTR(element, "id")) {
+ sendEvent.sendid = ATTR(element, "id");
+ } else {
+ /*
+ * The ids for <send> and <invoke> are subtly different. In a conformant
+ * SCXML document, they must be unique within the session, but in the case
+ * where the author does not provide them, the processor must generate a
+ * new unique ID not at load time but each time the element is executed.
+ * Furthermore the attribute 'idlocation' can be used to capture this
+ * automatically generated id. Finally note that the automatically generated
+ * id for <invoke> has a special format. See 6.4.1 Attribute Details for
+ * details. The SCXML processor may generate all other ids in any format,
+ * as long as they are unique.
+ */
+
+ /**
+ *
+ * If 'idlocation' is present, the SCXML Processor must generate an id when
+ * the parent <send> element is evaluated and store it in this location.
+ * See 3.14 IDs for details.
+ *
+ */
+ sendEvent.sendid = ATTR(getParentState(element), "id") + "." + UUID::getUUID();
+ if (HAS_ATTR(element, "idlocation")) {
+ _callbacks->assign(ATTR(element, "idlocation"), Data(sendEvent.sendid, Data::VERBATIM));
+ } else {
+ sendEvent.hideSendId = true;
+ }
+ }
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in send element idlocation", element);
+ }
+
+ try {
+ // delay
+ std::string delay;
+ if (HAS_ATTR(element, "delayexpr")) {
+ delay = _callbacks->evalAsData(ATTR(element, "delayexpr"));
+ } else if (HAS_ATTR(element, "delay")) {
+ delay = ATTR(element, "delay");
+ }
+ if (delay.size() > 0) {
+ NumAttr delayAttr(delay);
+ if (iequals(delayAttr.unit, "ms")) {
+ delayMs = strTo<uint32_t>(delayAttr.value);
+ } else if (iequals(delayAttr.unit, "s")) {
+ delayMs = strTo<double>(delayAttr.value) * 1000;
+ } else if (delayAttr.unit.length() == 0) { // unit less delay is interpreted as milliseconds
+ delayMs = strTo<uint32_t>(delayAttr.value);
+ } else {
+ LOG(ERROR) << "Cannot make sense of delay value " << delay << ": does not end in 's' or 'ms'";
+ }
+ }
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in send element delayexpr", element);
+ }
+
+ try {
+ // namelist
+ processNameLists(sendEvent.namelist, element);
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in send element namelist", element);
+ }
+
+
+ try {
+ // params
+ processParams(sendEvent.params, element);
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in send element param expr", element);
+ }
+
+ try {
+ // content
+ std::list<DOMElement*> contents = DOMUtils::filterChildElements(XML_PREFIX(element).str() + "content", element);
+ if (contents.size() > 0) {
+ sendEvent.data = elementAsData(contents.front());
+ }
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in send element content", element);
+ }
+
+ // if (sendReq->dom) {
+ // std::stringstream ss;
+ // ss << sendReq->dom;
+ // sendReq->xml = ss.str();
+ // _dataModel.replaceExpressions(sendReq->xml);
+ // }
+
+ // assert(_sendIds.find(sendReq->sendid) == _sendIds.end());
+ // _sendIds[sendReq->sendid] = std::make_pair(this, sendReq);
+
+ try {
+ _callbacks->checkValidSendType(type, target);
+ } catch (ErrorEvent e) {
+ e.data.compound["xpath"] = uscxml::Data(DOMUtils::xPathForNode(element), uscxml::Data::VERBATIM);
+ // test 332
+ e.sendid = sendEvent.sendid;
+ throw e;
+ }
+ _callbacks->enqueue(type, target, delayMs, sendEvent);
+
+}
+
+void BasicContentExecutorImpl::processCancel(xercesc::DOMElement* content) {
+ std::string sendid;
+ if (HAS_ATTR(content, "sendid")) {
+ sendid = ATTR(content, "sendid");
+ } else if (HAS_ATTR(content, "sendidexpr")) {
+ sendid = _callbacks->evalAsData(ATTR(content, "sendidexpr")).atom;
+ } else {
+ ERROR_EXECUTION_THROW2("Cancel element has neither sendid nor sendidexpr attribute", content);
+
+ }
+ _callbacks->cancelDelayed(sendid);
+}
+
+void BasicContentExecutorImpl::processIf(xercesc::DOMElement* content) {
+ bool blockIsTrue = _callbacks->isTrue(ATTR(content, "cond"));
+
+ DOMNodeList* children = content->getChildNodes();
+ for (unsigned int i = 0; i < children->getLength(); i++) {
+ if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE)
+ continue;
+
+ DOMElement* childElem = dynamic_cast<DOMElement*>(children->item(i));
+
+ if (iequals(TAGNAME(childElem), XML_PREFIX(content).str() + "elseif")) {
+ if (blockIsTrue) {
+ // last block was true, break here
+ break;
+ }
+ blockIsTrue = _callbacks->isTrue(ATTR(childElem, "cond"));
+ continue;
+ }
+ if (iequals(TAGNAME(childElem), XML_PREFIX(content).str() + "else")) {
+ if (blockIsTrue) {
+ // last block was true, break here
+ break;
+ }
+ blockIsTrue = true;
+ continue;
+ }
+
+ // current block is true
+ if (blockIsTrue) {
+ // we do now that the prefix of content is correct
+ process(childElem, XML_PREFIX(content));
+ }
+ }
+}
+
+void BasicContentExecutorImpl::processAssign(xercesc::DOMElement* content) {
+ std::string location = ATTR(content, "location");
+ _callbacks->assign(location, elementAsData(content));
+}
+
+void BasicContentExecutorImpl::processForeach(xercesc::DOMElement* content) {
+ std::string array = ATTR(content, "array");
+ std::string item = ATTR(content, "item");
+ std::string index = (HAS_ATTR(content, "index") ? ATTR(content, "index") : "");
+
+ uint32_t iterations = 0;
+ iterations = _callbacks->getLength(array);
+
+ for (uint32_t iteration = 0; iteration < iterations; iteration++) {
+ _callbacks->setForeach(item, array, index, iteration);
+
+ DOMNodeList* children = content->getChildNodes();
+ for (unsigned int i = 0; i < children->getLength(); i++) {
+ if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE)
+ continue;
+ process(dynamic_cast<DOMElement*>(children->item(i)), XML_PREFIX(content));
+ }
+ }
+}
+
+void BasicContentExecutorImpl::processLog(xercesc::DOMElement* content) {
+ std::string label = ATTR(content, "label");
+ std::string expr = ATTR(content, "expr");
+
+ Data d = _callbacks->evalAsData(expr);
+ if (label.size() > 0) {
+ std::cout << label << ": ";
+ }
+ std::cout << d << std::endl;
+}
+
+void BasicContentExecutorImpl::processScript(xercesc::DOMElement* content) {
+ // download as necessary
+ std::string scriptContent(X(content->getTextContent()));
+ _callbacks->evalAsData(scriptContent);
+
+}
+
+void BasicContentExecutorImpl::process(xercesc::DOMElement* block, const X& xmlPrefix) {
+ std::string tagName = TAGNAME(block);
+
+
+ if (iequals(tagName, xmlPrefix.str() + "onentry") ||
+ iequals(tagName, xmlPrefix.str() + "onexit") ||
+ iequals(tagName, xmlPrefix.str() + "transition")) {
+
+ DOMNodeList* children = block->getChildNodes();
+ try {
+ for(auto i = 0; i < children->getLength(); i++) {
+ if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE)
+ continue;
+ // process any child eleents
+ process(dynamic_cast<DOMElement*>(children->item(i)), xmlPrefix);
+ }
+ } catch (Event e) {
+ // there has been an error in an executable content block
+ // we do not care - parent scope has to handle it!
+ throw e;
+ }
+ return;
+ }
+
+ if (iequals(tagName, xmlPrefix.str() + "finalize")) {
+ std::list<DOMNode*> childElems = DOMUtils::filterChildType(DOMNode::ELEMENT_NODE, block, false);
+ if(childElems.size() > 0) {
+ for(auto elemIter = childElems.begin(); elemIter != childElems.end(); elemIter++) {
+ process(static_cast<DOMElement*>(*elemIter), xmlPrefix);
+ }
+ } else {
+ // issue 67 - empty finalize element
+ DOMNode* parent = block->getParentNode();
+ if (parent && parent->getNodeType() == DOMNode::ELEMENT_NODE) {
+ DOMElement* invokeElem = static_cast<DOMElement*>(parent);
+ if (iequals(X(invokeElem->getTagName()).str(), xmlPrefix.str() + "invoke")) {
+ // we are the empth finalize element of an invoke
+ // Specification 6.5.2: http://www.w3.org/TR/scxml/#N110EF
+
+ const Event& event = _callbacks->getCurrentEvent();
+ std::list<std::string> names = tokenize(ATTR(invokeElem, "namelist"));
+ for (std::list<std::string>::iterator nameIter = names.begin(); nameIter != names.end(); nameIter++) {
+ if (event.namelist.find(*nameIter) != event.namelist.end()) {
+ // scxml i/o proc keeps a dedicated namelist
+ _callbacks->assign(*nameIter, event.namelist.at(*nameIter));
+ } else if (event.data.compound.find(*nameIter) != event.data.compound.end()) {
+ // this is where it would end up with non scxml i/o processors
+ _callbacks->assign(*nameIter, event.data.compound.at(*nameIter));
+ }
+ }
+ }
+ }
+
+ }
+ return;
+ }
+
+ try {
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeExecutingContent, block);
+
+ if (false) {
+ } else if (iequals(tagName, xmlPrefix.str() + "raise")) {
+ processRaise(block);
+ } else if (iequals(tagName, xmlPrefix.str() + "send")) {
+ processSend(block);
+ } else if (iequals(tagName, xmlPrefix.str() + "cancel")) {
+ processCancel(block);
+ } else if (iequals(tagName, xmlPrefix.str() + "if")) {
+ processIf(block);
+ } else if (iequals(tagName, xmlPrefix.str() + "assign")) {
+ processAssign(block);
+ } else if (iequals(tagName, xmlPrefix.str() + "foreach")) {
+ processForeach(block);
+ } else if (iequals(tagName, xmlPrefix.str() + "log")) {
+ processLog(block);
+ } else if (iequals(tagName, xmlPrefix.str() + "script")) {
+ processScript(block);
+ } else {
+ LOG(ERROR) << tagName;
+ assert(false);
+ }
+ } catch (ErrorEvent exc) {
+
+ Event e(exc);
+ _callbacks->enqueueInternal(e);
+ LOG(ERROR) << exc << std::endl;
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), afterExecutingContent, block);
+
+ throw e; // will be catched in microstepper
+
+ }
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), afterExecutingContent, block);
+
+}
+
+void BasicContentExecutorImpl::invoke(xercesc::DOMElement* element) {
+ std::string type;
+ std::string source;
+ bool autoForward = false;
+ Event invokeEvent;
+
+ // type
+ if (HAS_ATTR(element, "typeexpr")) {
+ type = _callbacks->evalAsData(ATTR(element, "typeexpr")).atom;
+ } else if (HAS_ATTR(element, "type")) {
+ type = ATTR(element, "type");
+ } else {
+ // test 422
+ type = "http://www.w3.org/TR/scxml/";
+ }
+
+ // src
+ if (HAS_ATTR(element, "srcexpr")) {
+ source = _callbacks->evalAsData(ATTR(element, "srcexpr")).atom;
+ } else if (HAS_ATTR(element, "src")) {
+ source = ATTR(element, "src");
+ }
+ if (source.length() > 0) {
+ // absolutize url
+ }
+
+ // id
+ try {
+ if (HAS_ATTR(element, "id")) {
+ invokeEvent.invokeid = ATTR(element, "id");
+ } else {
+ invokeEvent.invokeid = ATTR(getParentState(element), "id") + "." + UUID::getUUID();
+ if (HAS_ATTR(element, "idlocation")) {
+ _callbacks->assign(ATTR(element, "idlocation"), Data(invokeEvent.invokeid, Data::VERBATIM));
+ }
+ }
+ // we need the invokeid to uninvoke - TODO: This is leaking!
+ char* invokeId = (char*)malloc(invokeEvent.invokeid.size() + 1);
+ memcpy(invokeId, invokeEvent.invokeid.c_str(), invokeEvent.invokeid.size());
+ invokeId[invokeEvent.invokeid.size()] = 0;
+
+ element->setUserData(X("invokeid"), (void*)invokeId, NULL);
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in invoke element idlocation", element);
+ }
+
+ try {
+ // namelist
+ processNameLists(invokeEvent.namelist, element);
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in send element namelist", element);
+ }
+
+
+ try {
+ // params
+ processParams(invokeEvent.params, element);
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in send element param expr", element);
+ }
+
+ try {
+ // content
+ std::list<DOMElement*> contents = DOMUtils::filterChildElements(XML_PREFIX(element).str() + "content", element);
+ if (contents.size() > 0) {
+ Data d = elementAsData(contents.front());
+ if (d.type == Data::INTERPRETED && d.atom.size() > 0) {
+ // immediately evaluate!
+ invokeEvent.data = _callbacks->evalAsData(d.atom);
+ } else {
+ invokeEvent.data = d;
+ }
+ }
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in send element content", element);
+ }
+
+ // autoforward
+ if (HAS_ATTR(element, "autoforward")) {
+ if (iequals(ATTR(element, "autoforward"), "true")) {
+ autoForward = true;
+ }
+ }
+
+ // finalize
+ DOMElement* finalize = NULL;
+ std::list<DOMElement*> finalizes = DOMUtils::filterChildElements(XML_PREFIX(element).str() + "finalize", element);
+ if (finalizes.size() > 0) {
+ finalize = finalizes.front();
+ }
+
+ USCXML_MONITOR_CALLBACK2(_callbacks->getMonitor(), beforeUninvoking, element, invokeEvent.invokeid);
+ _callbacks->invoke(type, source, autoForward, finalize, invokeEvent);
+ USCXML_MONITOR_CALLBACK2(_callbacks->getMonitor(), afterUninvoking, element, invokeEvent.invokeid);
+}
+
+void BasicContentExecutorImpl::uninvoke(xercesc::DOMElement* invoke) {
+ // TODO: DANGER This is the real danger here
+ char* invokeId = (char*)invoke->getUserData(X("invokeid"));
+ assert(invokeId != NULL);
+
+ USCXML_MONITOR_CALLBACK2(_callbacks->getMonitor(), beforeUninvoking, invoke, invokeId);
+ _callbacks->uninvoke(invokeId);
+ USCXML_MONITOR_CALLBACK2(_callbacks->getMonitor(), afterUninvoking, invoke, invokeId);
+
+ free(invokeId);
+}
+
+void BasicContentExecutorImpl::raiseDoneEvent(xercesc::DOMElement* state, xercesc::DOMElement* doneData) {
+
+ Event doneEvent;
+ doneEvent.name = "done.state.";
+ doneEvent.name += HAS_ATTR(state, "id") ? ATTR(state, "id") : DOMUtils::idForNode(state);
+
+ if (doneData != NULL) {
+ try {
+ try {
+ // namelist
+ processNameLists(doneEvent.namelist, doneData);
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in donedata element namelist", doneData);
+ }
+
+
+ try {
+ // params
+ processParams(doneEvent.params, doneData);
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in donedata element param expr", doneData);
+ }
+
+ try {
+ // content
+ std::list<DOMElement*> contents = DOMUtils::filterChildElements(XML_PREFIX(doneData).str() + "content", doneData);
+ if (contents.size() > 0) {
+ doneEvent.data = elementAsData(contents.front());
+ }
+ } catch (Event e) {
+ ERROR_EXECUTION_THROW2("Syntax error in donedata element content", doneData);
+ }
+
+ } catch (ErrorEvent exc) {
+ // clean out data test488 (needed?)
+ doneEvent.data = Data();
+
+ Event e(exc);
+ _callbacks->enqueueInternal(e);
+ // std::cout << exc << std::endl;
+ // throw e;
+ }
+ }
+
+ _callbacks->enqueueInternal(doneEvent);
+
+}
+
+void BasicContentExecutorImpl::processNameLists(std::map<std::string, Data>& nameMap, DOMElement* element) {
+ if (HAS_ATTR(element, "namelist")) {
+ std::list<std::string> names = tokenize(ATTR(element, "namelist"));
+ for (std::list<std::string>::const_iterator nameIter = names.begin(); nameIter != names.end(); nameIter++) {
+ nameMap[*nameIter] = _callbacks->evalAsData(*nameIter);
+ }
+ }
+}
+
+void BasicContentExecutorImpl::processParams(std::multimap<std::string, Data>& paramMap, DOMElement* element) {
+ std::list<DOMElement*> params = DOMUtils::filterChildElements(XML_PREFIX(element).str() + "param", element);
+ for (auto paramIter = params.begin(); paramIter != params.end(); paramIter++) {
+ std::string name = ATTR(*paramIter, "name");
+ Data d;
+ if (HAS_ATTR(*paramIter, "expr")) {
+ d = _callbacks->evalAsData(ATTR(*paramIter, "expr"));
+ } else if (HAS_ATTR(*paramIter, "location")) {
+ d = _callbacks->evalAsData(ATTR(*paramIter, "location"));
+ } else {
+ d = elementAsData(*paramIter);
+ }
+ paramMap.insert(make_pair(name, d));
+ }
+}
+
+Data BasicContentExecutorImpl::elementAsData(xercesc::DOMElement* element) {
+ if (HAS_ATTR(element, "expr")) {
+// return _callbacks->evalAsData(ATTR(element, "expr"));
+ if (LOCALNAME(element) == "content") {
+ // test 528
+ return _callbacks->evalAsData(ATTR(element, "expr"));
+ } else {
+ // test 326
+ return Data(ATTR(element, "expr"), Data::INTERPRETED);
+ }
+ }
+
+ if (HAS_ATTR(element, "src")) {
+ // remote content from URL
+
+ // test 446, test 552, test 558
+ std::string src = ATTR(element, "src");
+ URL url(ATTR(element, "src"));
+ if (!url.isAbsolute()) {
+ url = URL::resolve(url, _callbacks->getBaseURL());
+ }
+
+ std::string content = url.getInContent();
+
+ // make an attempt to parse as XML
+ try {
+ xercesc::XercesDOMParser* parser = new xercesc::XercesDOMParser();
+ parser->setValidationScheme(xercesc::XercesDOMParser::Val_Always);
+ parser->setDoNamespaces(true);
+ parser->useScanner(xercesc::XMLUni::fgWFXMLScanner);
+
+ xercesc::ErrorHandler* errHandler = new xercesc::HandlerBase();
+ parser->setErrorHandler(errHandler);
+
+ std::string tmp = url;
+ xercesc::MemBufInputSource is((XMLByte*)content.c_str(), content.size(), X("fake"));
+
+ parser->parse(is);
+
+ Data d;
+ xercesc::DOMDocument* doc = parser->adoptDocument();
+ d.adoptedDoc = std::make_shared<xercesc::DOMDocument*>(doc);
+ d.node = doc->getDocumentElement();
+ return d;
+
+ } catch (...) {
+ // just ignore and return as an interpreted string below
+ }
+ try {
+ Data d = _callbacks->getAsData(content);
+ if (!d.empty())
+ return d;
+ } catch(...) {}
+
+ return Data(spaceNormalize(content), Data::VERBATIM);
+
+ } else {
+ // local content in document
+
+ std::list<DOMNode*> elementChildren = DOMUtils::filterChildType(DOMNode::ELEMENT_NODE, element);
+ if (elementChildren.size() == 1) {
+ return Data(elementChildren.front());
+ } else if (elementChildren.size() > 1) {
+ return Data(element);
+ }
+
+ std::list<DOMNode*> textChildren = DOMUtils::filterChildType(DOMNode::TEXT_NODE, element);
+ if (textChildren.size() > 0) {
+ std::stringstream contentSS;
+ for (auto textIter = textChildren.begin(); textIter != textChildren.end(); textIter++) {
+ contentSS << X((*textIter)->getNodeValue());
+ }
+ try {
+ Data d = _callbacks->getAsData(contentSS.str());
+ if (!d.empty())
+ return d;
+ } catch(...) {}
+
+ return Data(spaceNormalize(contentSS.str()), Data::VERBATIM);
+ }
+ }
+
+ LOG(WARNING) << "Element " << DOMUtils::xPathForNode(element) << " did not yield any data";
+ return Data();
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/interpreter/ContentExecutorImpl.h b/src/uscxml/interpreter/ContentExecutorImpl.h
new file mode 100644
index 0000000..c0d28a2
--- /dev/null
+++ b/src/uscxml/interpreter/ContentExecutorImpl.h
@@ -0,0 +1,143 @@
+/**
+ * @file
+ * @author 2016 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+
+#ifndef CONTENTEXECUTORIMPL_H_13F2884F
+#define CONTENTEXECUTORIMPL_H_13F2884F
+
+#include "uscxml/Common.h"
+#include "uscxml/util/DOM.h"
+#include "uscxml/messages/Data.h"
+#include "uscxml/messages/Event.h"
+#include "uscxml/interpreter/InterpreterMonitor.h"
+#include <xercesc/dom/DOM.hpp>
+#include <string>
+
+namespace uscxml {
+
+class USCXML_API ContentExecutorCallbacks {
+public:
+ virtual void enqueueInternal(const Event& event) = 0;
+ virtual void enqueueExternal(const Event& event) = 0;
+ virtual void enqueueExternalDelayed(const Event& event, size_t delayMs, const std::string& eventUUID) = 0;
+ virtual void cancelDelayed(const std::string& eventId) = 0;
+
+ virtual bool isTrue(const std::string& expr) = 0;
+ virtual size_t getLength(const std::string& expr) = 0;
+
+ virtual void setForeach(const std::string& item,
+ const std::string& array,
+ const std::string& index,
+ uint32_t iteration) = 0;
+
+ virtual Data evalAsData(const std::string& expr) = 0;
+ virtual Data getAsData(const std::string& expr) = 0;
+ virtual void assign(const std::string& location, const Data& data) = 0;
+
+
+ virtual std::string getInvokeId() = 0;
+ virtual std::string getBaseURL() = 0;
+ virtual bool checkValidSendType(const std::string& type, const std::string& target) = 0;
+ virtual void enqueue(const std::string& type, const std::string& target, size_t delayMs, const Event& sendEvent) = 0;
+ virtual void invoke(const std::string& type, const std::string& src, bool autoForward, xercesc::DOMElement* finalize, const Event& invokeEvent) = 0;
+ virtual void uninvoke(const std::string& invokeId) = 0;
+
+ virtual const Event& getCurrentEvent() = 0;
+
+ /** Monitoring */
+ virtual InterpreterMonitor* getMonitor() = 0;
+
+};
+
+class USCXML_API ContentExecutorImpl {
+public:
+ ContentExecutorImpl(ContentExecutorCallbacks* callbacks) : _callbacks(callbacks) {}
+
+ virtual void process(xercesc::DOMElement* block, const X& xmlPrefix) = 0;
+
+ virtual void invoke(xercesc::DOMElement* invoke) = 0;
+ virtual void uninvoke(xercesc::DOMElement* invoke) = 0;
+
+ virtual void raiseDoneEvent(xercesc::DOMElement* state, xercesc::DOMElement* doneData) = 0;
+ virtual Data elementAsData(xercesc::DOMElement* element) = 0;
+
+protected:
+ ContentExecutorCallbacks* _callbacks;
+
+};
+
+class USCXML_API BasicContentExecutorImpl : public ContentExecutorImpl {
+public:
+ BasicContentExecutorImpl(ContentExecutorCallbacks* callbacks) : ContentExecutorImpl(callbacks) {}
+ virtual ~BasicContentExecutorImpl() {}
+
+ void processRaise(xercesc::DOMElement* content);
+ void processSend(xercesc::DOMElement* element);
+ void processCancel(xercesc::DOMElement* content);
+ void processIf(xercesc::DOMElement* content);
+ void processAssign(xercesc::DOMElement* content);
+ void processForeach(xercesc::DOMElement* content);
+ void processLog(xercesc::DOMElement* content);
+ void processScript(xercesc::DOMElement* content);
+
+ virtual void process(xercesc::DOMElement* block, const X& xmlPrefix);
+
+ virtual void invoke(xercesc::DOMElement* invoke);
+ virtual void uninvoke(xercesc::DOMElement* invoke);
+ virtual void raiseDoneEvent(xercesc::DOMElement* state, xercesc::DOMElement* doneData);
+
+ virtual Data elementAsData(xercesc::DOMElement* element);
+
+protected:
+ void processNameLists(std::map<std::string, Data>& nameMap, xercesc::DOMElement* element);
+ void processParams(std::multimap<std::string, Data>& paramMap, xercesc::DOMElement* element);
+
+};
+
+class USCXML_API ContentExecutor {
+public:
+ PIMPL_OPERATORS(ContentExecutor)
+
+ virtual void process(xercesc::DOMElement* block, const X& xmlPrefix) {
+ _impl->process(block, xmlPrefix);
+ }
+
+ virtual void invoke(xercesc::DOMElement* invoke) {
+ _impl->invoke(invoke);
+ }
+
+ virtual void uninvoke(xercesc::DOMElement* invoke) {
+ _impl->uninvoke(invoke);
+ }
+
+ virtual Data elementAsData(xercesc::DOMElement* element) {
+ return _impl->elementAsData(element);
+ }
+
+ virtual void raiseDoneEvent(xercesc::DOMElement* state, xercesc::DOMElement* doneData) {
+ return _impl->raiseDoneEvent(state, doneData);
+ }
+
+protected:
+ std::shared_ptr<ContentExecutorImpl> _impl;
+};
+
+}
+
+#endif /* end of include guard: CONTENTEXECUTORIMPL_H_13F2884F */
diff --git a/src/uscxml/interpreter/EventQueueImpl.cpp b/src/uscxml/interpreter/EventQueueImpl.cpp
new file mode 100644
index 0000000..345da69
--- /dev/null
+++ b/src/uscxml/interpreter/EventQueueImpl.cpp
@@ -0,0 +1,189 @@
+/**
+ * @file
+ * @author 2016 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#include "EventQueueImpl.h"
+#include <event2/util.h> // for evutil_socket_t
+#include <event2/thread.h>
+#include <assert.h>
+
+#include <easylogging++.h>
+
+namespace uscxml {
+
+EventQueueImpl::EventQueueImpl() {
+}
+EventQueueImpl::~EventQueueImpl() {
+}
+
+Event EventQueueImpl::dequeue(bool blocking) {
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
+ if (blocking) {
+ while (_queue.empty()) {
+ _cond.wait(_mutex);
+ }
+ }
+ if (_queue.size() > 0) {
+ Event event = _queue.front();
+ _queue.pop_front();
+// LOG(ERROR) << event.name;
+ return event;
+ }
+ return Event();
+
+}
+
+void EventQueueImpl::enqueue(const Event& event) {
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
+ _queue.push_back(event);
+ _cond.notify_all();
+}
+
+static void dummyCallback(evutil_socket_t fd, short what, void *arg) {
+ timeval tv;
+ tv.tv_sec = 365 * 24 * 3600;
+ tv.tv_usec = 0;
+ event *ev = *(event **)arg;
+ evtimer_add(ev, &tv);
+}
+
+DelayedEventQueueImpl::DelayedEventQueueImpl(DelayedEventQueueCallbacks* callbacks) {
+ _callbacks = callbacks;
+#ifndef _WIN32
+ evthread_use_pthreads();
+#else
+ evthread_use_windows_threads();
+#endif
+ _eventLoop = event_base_new();
+
+ // see here: https://github.com/named-data/ndn.cxx/blob/master/scheduler/scheduler.cc
+ // and here: https://www.mail-archive.com/libevent-users@seul.org/msg01676.html
+ timeval tv;
+ tv.tv_sec = 365 * 24 * 3600;
+ tv.tv_usec = 0;
+ _dummyEvent = evtimer_new(_eventLoop, dummyCallback, &_dummyEvent);
+ evtimer_add(_dummyEvent, &tv);
+
+ _thread = NULL;
+ _isStarted = false;
+ start();
+}
+
+DelayedEventQueueImpl::~DelayedEventQueueImpl() {
+ stop();
+ evtimer_del(_dummyEvent);
+ event_free(_dummyEvent);
+ event_base_free(_eventLoop);
+}
+
+void DelayedEventQueueImpl::timerCallback(evutil_socket_t fd, short what, void *arg) {
+ struct callbackData *data = (struct callbackData*)arg;
+ std::lock_guard<std::recursive_mutex> lock(data->eventQueue->_mutex);
+
+ if (data->eventQueue->_callbackData.find(data->eventUUID) == data->eventQueue->_callbackData.end())
+ return;
+
+ event_free(data->event);
+ data->eventQueue->_callbacks->eventReady(data->userData, data->eventUUID);
+ data->eventQueue->_callbackData.erase(data->eventUUID);
+}
+
+void DelayedEventQueueImpl::enqueueDelayed(const Event& event, size_t delayMs, const std::string& eventUUID) {
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
+ if(_callbackData.find(eventUUID) != _callbackData.end()) {
+ cancelDelayed(eventUUID);
+ }
+
+ _callbackData[eventUUID].eventUUID = eventUUID;
+ _callbackData[eventUUID].userData = event;
+ _callbackData[eventUUID].eventQueue = this;
+
+ struct timeval delay = {static_cast<int32_t>(delayMs / 1000), static_cast<int32_t>((delayMs % 1000) * 1000)};
+ struct event* e = event_new(_eventLoop, -1, 0, timerCallback, &_callbackData[eventUUID]);
+
+ _callbackData[eventUUID].event = e;
+
+ event_add(e, &delay);
+}
+
+void DelayedEventQueueImpl::cancelAllDelayed() {
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
+
+ while(_callbackData.size() > 0) {
+ std::pair<std::string, callbackData> item = *_callbackData.begin();
+ Event data = item.second.userData;
+ event_del(item.second.event);
+ event_free(item.second.event);
+ _callbackData.erase(item.first);
+ }
+
+}
+
+void DelayedEventQueueImpl::cancelDelayed(const std::string& eventId) {
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
+
+ if(_callbackData.find(eventId) != _callbackData.end()) {
+ event_del(_callbackData[eventId].event);
+ event_free(_callbackData[eventId].event);
+ _callbackData.erase(eventId);
+ }
+}
+
+void DelayedEventQueueImpl::run(void* instance) {
+ DelayedEventQueueImpl* INSTANCE = (DelayedEventQueueImpl*)instance;
+ int result;
+ while(INSTANCE->_isStarted) {
+ /**
+ * EVLOOP_NO_EXIT_ON_EMPTY was removed in libevent2.1 - we are
+ * using the event in the far future approach to get blocking
+ * behavior back (see comments in contructor)
+ */
+
+ // #ifndef EVLOOP_NO_EXIT_ON_EMPTY
+// result = event_base_dispatch(INSTANCE->_eventLoop);
+ // #else
+ // TODO: this is polling when no events are enqueued
+ result = event_base_loop(INSTANCE->_eventLoop, EVLOOP_ONCE);
+// assert(false); // NON-BLOCKING?!
+ //#endif
+ (void)result;
+ }
+}
+
+void DelayedEventQueueImpl::start() {
+ if (_isStarted) {
+ return;
+ }
+ _isStarted = true;
+ _thread = new std::thread(DelayedEventQueueImpl::run, this);
+}
+
+void DelayedEventQueueImpl::stop() {
+ if (_isStarted) {
+ _isStarted = false;
+ event_base_loopbreak(_eventLoop);
+ cancelAllDelayed();
+ }
+ if (_thread) {
+ _thread->join();
+ delete _thread;
+ _thread = NULL;
+ }
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/interpreter/EventQueueImpl.h b/src/uscxml/interpreter/EventQueueImpl.h
new file mode 100644
index 0000000..10543c9
--- /dev/null
+++ b/src/uscxml/interpreter/EventQueueImpl.h
@@ -0,0 +1,125 @@
+/**
+ * @file
+ * @author 2016 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#ifndef EVENTSOURCE_H_775AB206
+#define EVENTSOURCE_H_775AB206
+
+#include "uscxml/Common.h"
+#include "uscxml/messages/Event.h"
+#include <string>
+#include <map>
+#include <list>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+
+#include <event2/event.h>
+
+
+namespace uscxml {
+
+class USCXML_API EventQueueImpl {
+public:
+ EventQueueImpl();
+ virtual ~EventQueueImpl();
+ virtual Event dequeue(bool blocking);
+ virtual void enqueue(const Event& event);
+
+protected:
+ std::list<Event> _queue;
+ std::recursive_mutex _mutex;
+ std::condition_variable_any _cond;
+
+};
+
+class USCXML_API DelayedEventQueueCallbacks {
+public:
+ virtual void eventReady(Event& event, const std::string& eventId) = 0;
+};
+
+class USCXML_API DelayedEventQueueImpl : public EventQueueImpl {
+public:
+ DelayedEventQueueImpl(DelayedEventQueueCallbacks* callbacks);
+ virtual ~DelayedEventQueueImpl();
+ virtual void enqueueDelayed(const Event& event, size_t delayMs, const std::string& eventUUID);
+ virtual void cancelDelayed(const std::string& eventId);
+ virtual void cancelAllDelayed();
+
+protected:
+ struct callbackData {
+ Event userData;
+ std::string eventUUID;
+ bool persist;
+ struct event *event;
+ DelayedEventQueueImpl* eventQueue;
+ };
+
+ bool _isStarted;
+ std::thread* _thread;
+
+ std::map<std::string, callbackData> _callbackData;
+ struct event_base* _eventLoop;
+ struct event* _dummyEvent;
+
+ static void run(void* instance);
+ void start();
+ void stop();
+
+ static void timerCallback(evutil_socket_t fd, short what, void *arg);
+ DelayedEventQueueCallbacks* _callbacks;
+};
+
+class USCXML_API EventQueue {
+public:
+ PIMPL_OPERATORS(EventQueue)
+
+ virtual Event dequeue(bool blocking) {
+ return _impl->dequeue(blocking);
+ }
+ virtual void enqueue(const Event& event) {
+ return _impl->enqueue(event);
+ }
+
+protected:
+ std::shared_ptr<EventQueueImpl> _impl;
+
+};
+
+class USCXML_API DelayedEventQueue : public EventQueue {
+public:
+ PIMPL_OPERATORS2(DelayedEventQueue, EventQueue)
+
+ void enqueueDelayed(const Event& event, size_t delayMs, const std::string& eventUUID) {
+ _impl->enqueueDelayed(event, delayMs, eventUUID);
+ }
+ void cancelDelayed(const std::string& eventUUID) {
+ return _impl->cancelDelayed(eventUUID);
+ }
+
+ void cancelAllDelayed() {
+ return _impl->cancelAllDelayed();
+ }
+
+protected:
+ std::shared_ptr<DelayedEventQueueImpl> _impl;
+};
+
+}
+
+#endif /* end of include guard: EVENTSOURCE_H_775AB206 */
diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp
deleted file mode 100644
index 6084641..0000000
--- a/src/uscxml/interpreter/InterpreterDraft6.cpp
+++ /dev/null
@@ -1,573 +0,0 @@
-/**
- * @file
- * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
- * @copyright Simplified BSD
- *
- * @cond
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the FreeBSD license as published by the FreeBSD
- * project.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * You should have received a copy of the FreeBSD license along with this
- * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
- * @endcond
- */
-
-#include "InterpreterDraft6.h"
-#include "uscxml/concurrency/DelayedEventQueue.h"
-
-#include <glog/logging.h>
-#include "uscxml/UUID.h"
-#include "uscxml/dom/DOMUtils.h"
-
-#define VERBOSE 0
-
-namespace uscxml {
-
-using namespace Arabica::XPath;
-using namespace Arabica::DOM;
-
-// see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation
-
-
-Arabica::XPath::NodeSet<std::string> InterpreterDraft6::removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- Arabica::XPath::NodeSet<std::string> filteredTransitions;
- for (unsigned int i = 0; i < enabledTransitions.size(); i++) {
- Element<std::string> t(enabledTransitions[i]);
- if (!isTargetless(t)) {
- for (unsigned int j = 0; j < filteredTransitions.size(); j++) {
- if (j == i)
- continue;
- Element<std::string> t2(filteredTransitions[j]);
- if (isPreemptingTransition(t2, t)) {
-#if 0
- std::cout << "#####" << std::endl << "Transition " << std::endl
- << t2 << std::endl << " preempts " << std::endl << t << std::endl << "#####" << std::endl;
-#endif
- goto LOOP;
- }
- }
- }
-#if 0
- std::cout << "----" << "Enabling Transition " << std::endl << t << std::endl;
-#endif
-
- filteredTransitions.push_back(t);
-LOOP:
- ;
- }
- return filteredTransitions;
-}
-
-
-
-/**
- * Is t1 preempting t2?
- */
-bool InterpreterDraft6::isPreemptingTransition(const Element<std::string>& t1, const Element<std::string>& t2) {
- assert(t1);
- assert(t2);
-
-#if 0
- std::cout << "Checking preemption: " << std::endl << t1 << std::endl << t2 << std::endl;
-#endif
-
- if (t1 == t2)
- return false;
- if (isTargetless(t1))
- return false; // targetless transitions do not preempt any other transitions
- if (isWithinParallel(t1) && isCrossingBounds(t2))
- return true; // transitions within a single child of <parallel> preempt transitions that cross child boundaries
- if (isCrossingBounds(t1))
- return true; // transitions that cross child boundaries preempt all other transitions
-
- return false;
-}
-
-bool InterpreterDraft6::isCrossingBounds(const Element<std::string>& transition) {
- if (!isTargetless(transition) && !isWithinParallel(transition))
- return true;
- return false;
-}
-
-bool InterpreterDraft6::isWithinParallel(const Element<std::string>& transition) {
- if (isTargetless(transition))
- return false;
-
- if (_transWithinParallel.find(transition) != _transWithinParallel.end())
- return _transWithinParallel[transition];
-
- Node<std::string> source;
- if (HAS_ATTR(transition, "type") && iequals(ATTR(transition, "type"), "internal")) {
- source = getSourceState(transition);
- } else {
- source = getSourceState(transition).getParentNode();
- }
- NodeSet<std::string> targets = getTargetStates(transition);
- targets.push_back(source);
-
- Node<std::string> lcpa = findLCPA(targets);
- _transWithinParallel[transition] = lcpa;
-
- return _transWithinParallel[transition];
-}
-
-Node<std::string> InterpreterDraft6::findLCPA(const Arabica::XPath::NodeSet<std::string>& states) {
- Arabica::XPath::NodeSet<std::string> ancestors = getProperAncestors(states[0], Node<std::string>());
- Node<std::string> ancestor;
- for (size_t i = 0; i < ancestors.size(); i++) {
- if (!isParallel(Element<std::string>(ancestors[i])))
- continue;
- for (size_t j = 0; j < states.size(); j++) {
- if (!isDescendant(states[j], ancestors[i]) && (states[j] != ancestors[i]))
- goto NEXT_ANCESTOR;
- }
- ancestor = ancestors[i];
- break;
-NEXT_ANCESTOR:
- ;
- }
- return ancestor;
-}
-
-
-
-void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- NodeSet<std::string> statesToExit;
-
-#if VERBOSE
- std::cout << _name << ": Enabled exit transitions: " << std::endl;
- for (size_t i = 0; i < enabledTransitions.size(); i++) {
- std::cout << enabledTransitions[i] << std::endl;
- }
- std::cout << std::endl;
-#endif
-
- for (size_t i = 0; i < enabledTransitions.size(); i++) {
- Element<std::string> t = ((Element<std::string>)enabledTransitions[i]);
- if (!isTargetless(t)) {
- Node<std::string> ancestor;
- Node<std::string> source = getSourceState(t);
-// std::cout << t << std::endl << TAGNAME(t) << std::endl;
- NodeSet<std::string> tStates = getTargetStates(t);
- bool isInternal = (HAS_ATTR(t, "type") && iequals(ATTR(t, "type"), "internal")); // external is default
- bool allDescendants = true;
- for (size_t j = 0; j < tStates.size(); j++) {
- if (!isDescendant(tStates[j], source)) {
- allDescendants = false;
- break;
- }
- }
- if (isInternal && allDescendants && isCompound(Element<std::string>(source))) {
- ancestor = source;
- } else {
- NodeSet<std::string> tmpStates;
- tmpStates.push_back(source);
- tmpStates.insert(tmpStates.end(), tStates.begin(), tStates.end());
-#if 0
- std::cout << _name << ": tmpStates: ";
- for (size_t i = 0; i < tmpStates.size(); i++) {
- std::cout << ATTR(tmpStates[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
- ancestor = findLCCA(tmpStates);
- }
-#if 0
- std::cout << _name << ": Ancestor: " << ATTR(ancestor, "id") << std::endl;;
-#endif
- for (size_t j = 0; j < _configuration.size(); j++) {
- if (isDescendant(_configuration[j], ancestor))
- statesToExit.push_back(_configuration[j]);
- }
- }
- }
- // remove statesToExit from _statesToInvoke
- std::list<Node<std::string> > tmp;
- for (size_t i = 0; i < _statesToInvoke.size(); i++) {
- if (!isMember(_statesToInvoke[i], statesToExit)) {
- tmp.push_back(_statesToInvoke[i]);
- }
- }
- _statesToInvoke = NodeSet<std::string>();
- _statesToInvoke.insert(_statesToInvoke.end(), tmp.begin(), tmp.end());
-
- statesToExit.forward(false);
- statesToExit.sort();
-
-#if 0
- std::cout << _name << ": States to exit: ";
- for (size_t i = 0; i < statesToExit.size(); i++) {
- std::cout << LOCALNAME(statesToExit[i]) << ":" << ATTR(statesToExit[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- for (size_t i = 0; i < statesToExit.size(); i++) {
- NodeSet<std::string> histories = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "history", statesToExit[i]);
- for (size_t j = 0; j < histories.size(); j++) {
- Element<std::string> historyElem = (Element<std::string>)histories[j];
- std::string historyType = (historyElem.hasAttribute("type") ? historyElem.getAttribute("type") : "shallow");
- NodeSet<std::string> historyNodes;
- for (size_t k = 0; k < _configuration.size(); k++) {
- if (iequals(historyType, "deep")) {
- if (isAtomic(Element<std::string>(_configuration[k])) && isDescendant(_configuration[k], statesToExit[i]))
- historyNodes.push_back(_configuration[k]);
- } else {
- if (_configuration[k].getParentNode() == statesToExit[i])
- historyNodes.push_back(_configuration[k]);
- }
- }
- _historyValue[historyElem.getAttribute("id")] = historyNodes;
-#if VERBOSE
- std::cout << _name << ": History node " << ATTR(historyElem, "id") << " contains: ";
- for (size_t i = 0; i < historyNodes.size(); i++) {
- std::cout << ATTR_CAST(historyNodes[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- }
- }
-
- for (size_t i = 0; i < statesToExit.size(); i++) {
- USCXML_MONITOR_CALLBACK3(beforeExitingState, Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size()))
-
- NodeSet<std::string> onExits = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "onExit", statesToExit[i]);
- for (size_t j = 0; j < onExits.size(); j++) {
- Element<std::string> onExitElem = (Element<std::string>)onExits[j];
- executeContent(onExitElem);
- }
-
- USCXML_MONITOR_CALLBACK3(afterExitingState, Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size()))
-
- NodeSet<std::string> invokes = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]);
- for (size_t j = 0; j < invokes.size(); j++) {
- Element<std::string> invokeElem = (Element<std::string>)invokes[j];
- if (HAS_ATTR(invokeElem, "persist") && stringIsTrue(ATTR(invokeElem, "persist"))) {
- // extension for flattened SCXML documents, we will need an explicit uninvoke element
- } else {
- cancelInvoke(invokeElem);
- }
- }
-
- // remove statesToExit[i] from _configuration - test409
- tmp.clear();
- for (size_t j = 0; j < _configuration.size(); j++) {
- if (_configuration[j] != statesToExit[i]) {
- tmp.push_back(_configuration[j]);
- }
- }
- _configuration = NodeSet<std::string>();
- _configuration.insert(_configuration.end(), tmp.begin(), tmp.end());
- }
-}
-
-void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- NodeSet<std::string> statesToEnter;
- NodeSet<std::string> statesForDefaultEntry;
- // initialize the temporary table for default content in history states
- NodeSet<std::string> defaultHistoryContent;
-
-#if VERBOSE
- std::cout << _name << ": Enabled enter transitions: " << std::endl;
- for (size_t i = 0; i < enabledTransitions.size(); i++) {
- std::cout << "\t" << enabledTransitions[i] << std::endl;
- }
- std::cout << std::endl;
-#endif
-
- for (size_t i = 0; i < enabledTransitions.size(); i++) {
- Element<std::string> transition = ((Element<std::string>)enabledTransitions[i]);
- if (!isTargetless(transition)) {
- std::string transitionType = (iequals(transition.getAttribute("type"), "internal") ? "internal" : "external");
- NodeSet<std::string> tStates = getTargetStates(transition);
-
-#if VERBOSE
- std::cout << _name << ": Target States: ";
- for (size_t i = 0; i < tStates.size(); i++) {
- std::cout << ATTR_CAST(tStates[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- Node<std::string> ancestor;
- Node<std::string> source = getSourceState(transition);
-#if VERBOSE
- std::cout << _name << ": Source States: " << ATTR_CAST(source, "id") << std::endl;
-#endif
- assert(source);
-
- bool allDescendants = true;
- for (size_t j = 0; j < tStates.size(); j++) {
- if (!isDescendant(tStates[j], source)) {
- allDescendants = false;
- break;
- }
- }
- if (iequals(transitionType, "internal") &&
- isCompound(Element<std::string>(source)) &&
- allDescendants) {
- ancestor = source;
- } else {
- NodeSet<std::string> tmpStates;
- tmpStates.push_back(source);
- tmpStates.insert(tmpStates.end(), tStates.begin(), tStates.end());
-
- ancestor = findLCCA(tmpStates);
- }
-
-#if VERBOSE
- std::cout << _name << ": Ancestor: " << ATTR_CAST(ancestor, "id") << std::endl;
-#endif
-
- for (size_t j = 0; j < tStates.size(); j++) {
- addStatesToEnter(Element<std::string>(tStates[j]), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
-
-#if VERBOSE
- std::cout << _name << ": States to enter: ";
- for (size_t i = 0; i < statesToEnter.size(); i++) {
- std::cout << LOCALNAME(statesToEnter[i]) << ":" << ATTR_CAST(statesToEnter[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- for (size_t j = 0; j < tStates.size(); j++) {
- NodeSet<std::string> ancestors = getProperAncestors(tStates[j], ancestor);
-
-#if VERBOSE
- std::cout << _name << ": Proper Ancestors of " << ATTR_CAST(tStates[j], "id") << " and " << ATTR_CAST(ancestor, "id") << ": ";
- for (size_t i = 0; i < ancestors.size(); i++) {
- std::cout << ATTR_CAST(ancestors[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- for (size_t k = 0; k < ancestors.size(); k++) {
- statesToEnter.push_back(ancestors[k]);
- if(isParallel(Element<std::string>(ancestors[k]))) {
- NodeSet<std::string> childs = getChildStates(ancestors[k]);
- for (size_t l = 0; l < childs.size(); l++) {
- bool someIsDescendant = false;
- for (size_t m = 0; m < statesToEnter.size(); m++) {
- if (isDescendant(statesToEnter[m], childs[l])) {
- someIsDescendant = true;
- break;
- }
- }
- if (!someIsDescendant) {
- addStatesToEnter(Element<std::string>(childs[l]), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
- }
- }
- }
- }
- }
- }
- statesToEnter.to_document_order();
-
-#if VERBOSE
- std::cout << _name << ": States to enter: ";
- for (size_t i = 0; i < statesToEnter.size(); i++) {
- std::cout << ATTR_CAST(statesToEnter[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- for (size_t i = 0; i < statesToEnter.size(); i++) {
- Element<std::string> stateElem = (Element<std::string>)statesToEnter[i];
-
- // extension for flattened interpreters
- for (unsigned int k = 0; k < statesToEnter.size(); k++) {
- NodeSet<std::string> invokes = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToEnter[k]);
- for (unsigned int j = 0; j < invokes.size(); j++) {
- Element<std::string> invokeElem = Element<std::string>(invokes[j]);
- if (HAS_ATTR(invokeElem, "persist") && stringIsTrue(ATTR(invokeElem, "persist"))) {
- invoke(invokeElem);
- }
- }
- }
-
- USCXML_MONITOR_CALLBACK3(beforeEnteringState, stateElem, (i + 1 < statesToEnter.size()))
-
- // extension for flattened SCXML documents, we will need an explicit uninvoke element
- NodeSet<std::string> uninvokes = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "uninvoke", statesToEnter[i]);
- for (size_t j = 0; j < uninvokes.size(); j++) {
- Element<std::string> uninvokeElem = (Element<std::string>)uninvokes[j];
- cancelInvoke(uninvokeElem);
- }
-
- _configuration.push_back(stateElem);
- _statesToInvoke.push_back(stateElem);
-
- if (_binding == LATE && !isMember(stateElem, _alreadyEntered)) {
- NodeSet<std::string> dataModelElems = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "datamodel", stateElem);
- if(dataModelElems.size() > 0 && _dataModel) {
- Arabica::XPath::NodeSet<std::string> dataElems = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "data", dataModelElems[0]);
- for (size_t j = 0; j < dataElems.size(); j++) {
- if (dataElems[j].getNodeType() == Node_base::ELEMENT_NODE)
- initializeData(Element<std::string>(dataElems[j]));
- }
- }
- _alreadyEntered.push_back(stateElem);
- }
- // execute onentry executable content
- NodeSet<std::string> onEntryElems = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "onEntry", stateElem);
- executeContent(onEntryElems, false);
-
- USCXML_MONITOR_CALLBACK3(afterEnteringState, stateElem, (i + 1 < statesToEnter.size()))
-
- if (isMember(stateElem, statesForDefaultEntry)) {
- // execute initial transition content for compound states
- Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", stateElem).asNodeSet();
- for (size_t j = 0; j < transitions.size(); j++) {
- executeContent(Element<std::string>(transitions[j]));
- }
- }
-
-#if 0
- // not working yet
- if (isMember(stateElem, defaultHistoryContent)) {
- // execute history transition
- Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _nsInfo.xpathPrefix + "history/" + _nsInfo.xpathPrefix + "transition", stateElem).asNodeSet();
- for (size_t j = 0; j < transitions.size(); j++) {
- executeContent(transitions[j]);
- }
- }
-#endif
- if (isFinal(stateElem)) {
-
- Arabica::DOM::Element<std::string> doneData;
- Arabica::XPath::NodeSet<std::string> doneDatas = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "donedata", stateElem);
- if (doneDatas.size() > 0) {
- // only process first donedata element
- doneData = Element<std::string>(doneDatas[0]);
- }
-
- internalDoneSend(stateElem, doneData);
- Node<std::string> parent = stateElem.getParentNode();
-
- if (parent.getNodeType() == Node_base::ELEMENT_NODE &&
- parent.getParentNode().getNodeType() == Node_base::ELEMENT_NODE &&
- isParallel(Element<std::string>(parent.getParentNode()))) {
- Element<std::string> grandParent = (Element<std::string>)parent.getParentNode();
-
- Arabica::XPath::NodeSet<std::string> childs = getChildStates(grandParent);
- bool inFinalState = true;
- for (size_t j = 0; j < childs.size(); j++) {
- if (!isInFinalState(Element<std::string>(childs[j]))) {
- inFinalState = false;
- break;
- }
- }
- if (inFinalState) {
- internalDoneSend(Element<std::string>(parent), Arabica::DOM::Element<std::string>());
- }
- }
- }
- }
- for (size_t i = 0; i < _configuration.size(); i++) {
- Element<std::string> stateElem = (Element<std::string>)_configuration[i];
- if (isFinal(stateElem) && parentIsScxmlState(stateElem)) {
- _topLevelFinalReached = true;
- }
- }
-}
-
-void InterpreterDraft6::addStatesToEnter(const Element<std::string>& state,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
- Arabica::XPath::NodeSet<std::string>& defaultHistoryContent) {
- std::string stateId = ((Element<std::string>)state).getAttribute("id");
-
-#if VERBOSE
- std::cout << "Adding state to enter: " << stateId << std::endl;
-#endif
- if (isHistory(state)) {
- if (_historyValue.find(stateId) != _historyValue.end()) {
- Arabica::XPath::NodeSet<std::string> historyValue = _historyValue[stateId];
-
-#if VERBOSE
- std::cout << "History State " << ATTR(state, "id") << ": ";
- for (size_t i = 0; i < historyValue.size(); i++) {
- std::cout << ATTR_CAST(historyValue[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- for (size_t i = 0; i < historyValue.size(); i++) {
- addStatesToEnter(Element<std::string>(historyValue[i]), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- NodeSet<std::string> ancestors = getProperAncestors(historyValue[i], state);
-
-#if VERBOSE
- std::cout << "Proper Ancestors: ";
- for (size_t j = 0; j < ancestors.size(); j++) {
- std::cout << ATTR_CAST(ancestors[j], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- for (size_t j = 0; j < ancestors.size(); j++) {
- statesToEnter.push_back(ancestors[j]);
- }
- }
- } else {
- defaultHistoryContent.push_back(getParentState(state));
- NodeSet<std::string> transitions = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "transition", state);
- for (size_t i = 0; i < transitions.size(); i++) {
- NodeSet<std::string> targets = getTargetStates(Element<std::string>(transitions[i]));
- for (size_t j = 0; j < targets.size(); j++) {
- addStatesToEnter(Element<std::string>(targets[j]), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
-
- // Modifications from chris nuernberger
- NodeSet<std::string> ancestors = getProperAncestors(targets[j], state);
- for (size_t k = 0; k < ancestors.size(); k++) {
- statesToEnter.push_back(ancestors[k]);
- }
- }
- }
- }
- } else {
- statesToEnter.push_back(state);
- if (isCompound(state)) {
- statesForDefaultEntry.push_back(state);
-
- NodeSet<std::string> tStates = getInitialStates(state);
- for (size_t i = 0; i < tStates.size(); i++) {
- addStatesToEnter(Element<std::string>(tStates[i]), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
-
- // addStatesToEnter(getInitialState(state), statesToEnter, statesForDefaultEntry);
- // NodeSet<std::string> tStates = getTargetStates(getInitialState(state));
-
- } else if(isParallel(state)) {
- NodeSet<std::string> childStates = getChildStates(state);
- for (size_t i = 0; i < childStates.size(); i++) {
- addStatesToEnter(Element<std::string>(childStates[i]), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
- }
- }
-}
-
-void InterpreterDraft6::handleDOMEvent(Arabica::DOM::Events::Event<std::string>& event) {
- InterpreterImpl::handleDOMEvent(event);
-
- // remove modified states from cache
- if (event.getType().compare("DOMAttrModified") == 0) // we do not care about attributes
- return;
- Node<std::string> target = Arabica::DOM::Node<std::string>(event.getTarget());
- NodeSet<std::string> transitions = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "transition", target, true);
- for (size_t i = 0; i < transitions.size(); i++) {
- const Element<std::string> transElem = Element<std::string>(transitions[i]);
- if (_transWithinParallel.find(transElem) != _transWithinParallel.end())
- _transWithinParallel.erase(transElem);
- }
-}
-
-
-} \ No newline at end of file
diff --git a/src/uscxml/interpreter/InterpreterDraft6.h b/src/uscxml/interpreter/InterpreterDraft6.h
deleted file mode 100644
index 6a1275b..0000000
--- a/src/uscxml/interpreter/InterpreterDraft6.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * @file
- * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
- * @copyright Simplified BSD
- *
- * @cond
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the FreeBSD license as published by the FreeBSD
- * project.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * You should have received a copy of the FreeBSD license along with this
- * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
- * @endcond
- */
-
-#ifndef INTERPRETERDRAFT6_H_JAXK9FE1
-#define INTERPRETERDRAFT6_H_JAXK9FE1
-
-#include "uscxml/Interpreter.h"
-
-namespace uscxml {
-
-class USCXML_API InterpreterDraft6 : public InterpreterImpl {
-public:
- virtual ~InterpreterDraft6() {};
-
-protected:
-
- void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
- void addStatesToEnter(const Arabica::DOM::Element<std::string>& state,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
- Arabica::XPath::NodeSet<std::string>& defaultHistoryContent);
-
- void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
-
- Arabica::XPath::NodeSet<std::string> removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
- bool isPreemptingTransition(const Arabica::DOM::Element<std::string>& t1, const Arabica::DOM::Element<std::string>& t2);
-
- bool isCrossingBounds(const Arabica::DOM::Element<std::string>& transition);
- bool isWithinParallel(const Arabica::DOM::Element<std::string>& transition);
- Arabica::DOM::Node<std::string> findLCPA(const Arabica::XPath::NodeSet<std::string>& states);
-
- std::map<Arabica::DOM::Element<std::string>, bool> _transWithinParallel; // this is costly to calculate
-
- virtual void handleDOMEvent(Arabica::DOM::Events::Event<std::string>& event);
-
-};
-
-}
-
-#endif /* end of include guard: INTERPRETERDRAFT6_H_JAXK9FE1 */
diff --git a/src/uscxml/interpreter/InterpreterFast.cpp b/src/uscxml/interpreter/InterpreterFast.cpp
deleted file mode 100644
index ab5dce0..0000000
--- a/src/uscxml/interpreter/InterpreterFast.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * @file
- * @author 2016 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
- * @copyright Simplified BSD
- *
- * @cond
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the FreeBSD license as published by the FreeBSD
- * project.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * You should have received a copy of the FreeBSD license along with this
- * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
- * @endcond
- */
-
-#include "InterpreterFast.h"
-
-#include "uscxml/Factory.h"
-#include "uscxml/concurrency/DelayedEventQueue.h"
-
-#include <glog/logging.h>
-#include "uscxml/UUID.h"
-#include "uscxml/dom/DOMUtils.h"
-
-namespace uscxml {
-
-using namespace Arabica::XPath;
-using namespace Arabica::DOM;
-
-
-void InterpreterFast::handleDOMEvent(Arabica::DOM::Events::Event<std::string>& event) {
- InterpreterImpl::handleDOMEvent(event);
-
- if (event.getType().compare("DOMAttrModified") == 0) // we do not care about attributes
- return;
-
-}
-} \ No newline at end of file
diff --git a/src/uscxml/interpreter/InterpreterFast.h b/src/uscxml/interpreter/InterpreterFast.h
deleted file mode 100644
index 5838dc0..0000000
--- a/src/uscxml/interpreter/InterpreterFast.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * @file
- * @author 2016 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
- * @copyright Simplified BSD
- *
- * @cond
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the FreeBSD license as published by the FreeBSD
- * project.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * You should have received a copy of the FreeBSD license along with this
- * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
- * @endcond
- */
-
-#ifndef INTERPRETERFAST_H_224A5F07
-#define INTERPRETERFAST_H_224A5F07
-
-#include "uscxml/Interpreter.h"
-
-namespace uscxml {
-
-class InterpreterFast : public InterpreterImpl {
-protected:
- virtual void setupSets();
- virtual void handleDOMEvent(Arabica::DOM::Events::Event<std::string>& event);
-
-private:
-
- /* TODO: use post-order and document-order per STL comparator (sorted std::set?) */
-
- std::vector<Arabica::XPath::NodeSet<std::string> > _states;
- std::vector<Arabica::XPath::NodeSet<std::string> > _transitions;
-
- std::vector<std::vector<bool> > _conflictingTransitions;
- std::vector<std::vector<bool> > _exitSets;
- std::vector<std::vector<bool> > _targetSets;
-
-};
-
-}
-
-#endif /* end of include guard: INTERPRETERFAST_H_224A5F07 */
diff --git a/src/uscxml/interpreter/InterpreterImpl.cpp b/src/uscxml/interpreter/InterpreterImpl.cpp
new file mode 100644
index 0000000..ba75ab8
--- /dev/null
+++ b/src/uscxml/interpreter/InterpreterImpl.cpp
@@ -0,0 +1,361 @@
+/**
+ * @file
+ * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#include "uscxml/Common.h"
+#include "uscxml/util/UUID.h"
+#include "uscxml/Interpreter.h"
+#include "uscxml/messages/Event.h"
+#include "uscxml/util/String.h"
+#include "uscxml/util/Predicates.h"
+
+#include "easylogging++.h"
+
+#include <iostream>
+
+#include <assert.h>
+#include <algorithm>
+#include <memory>
+#include <mutex>
+
+#include "uscxml/interpreter/MicroStepFast.h"
+
+#define VERBOSE 0
+
+namespace uscxml {
+
+using namespace xercesc;
+
+std::map<std::string, std::weak_ptr<InterpreterImpl> > InterpreterImpl::_instances;
+std::recursive_mutex InterpreterImpl::_instanceMutex;
+
+std::map<std::string, std::weak_ptr<InterpreterImpl> > InterpreterImpl::getInstances() {
+ std::lock_guard<std::recursive_mutex> lock(_instanceMutex);
+ std::map<std::string, std::weak_ptr<InterpreterImpl> >::iterator instIter = _instances.begin();
+ while(instIter != _instances.end()) {
+ if (!instIter->second.lock()) {
+ _instances.erase(instIter++);
+ } else {
+ instIter++;
+ }
+ }
+ return _instances;
+}
+
+void InterpreterImpl::addInstance(std::shared_ptr<InterpreterImpl> interpreterImpl) {
+ std::lock_guard<std::recursive_mutex> lock(_instanceMutex);
+ assert(_instances.find(interpreterImpl->getSessionId()) == _instances.end());
+ _instances[interpreterImpl->getSessionId()] = interpreterImpl;
+}
+
+InterpreterImpl::InterpreterImpl() : _isInitialized(false), _document(NULL), _scxml(NULL), _state(USCXML_INSTANTIATED), _monitor(NULL) {
+ try {
+ xercesc::XMLPlatformUtils::Initialize();
+ } catch (const xercesc::XMLException& toCatch) {
+ ERROR_PLATFORM_THROW("Cannot initialize XercesC: " + X(toCatch.getMessage()).str());
+ }
+
+ _sessionId = UUID::getUUID();
+ _factory = Factory::getInstance();
+}
+
+
+InterpreterImpl::~InterpreterImpl() {
+ if (_delayQueue)
+ _delayQueue.cancelAllDelayed();
+ if (_document)
+ delete _document;
+ {
+ std::lock_guard<std::recursive_mutex> lock(_instanceMutex);
+ _instances.erase(getSessionId());
+ }
+}
+
+void InterpreterImpl::cancel() {
+ _microStepper.markAsCancelled();
+ Event unblock;
+ enqueueExternal(unblock);
+}
+
+
+void InterpreterImpl::setupDOM() {
+
+ if (!_document) {
+ ERROR_PLATFORM_THROW("Interpreter has no XML document");
+ }
+
+ if (!_scxml) {
+ // find scxml element
+ DOMNodeList* scxmls = NULL;
+
+ // proper namespace
+ scxmls = _document->getElementsByTagNameNS(X("http://www.w3.org/2005/07/scxml"), X("scxml"));
+ if (scxmls->getLength() > 0)
+ goto SCXML_STOP_SEARCH;
+
+ // no namespace
+ scxmls = _document->getElementsByTagName(X("scxml"));
+ if (scxmls->getLength() > 0)
+ goto SCXML_STOP_SEARCH;
+
+SCXML_STOP_SEARCH:
+ if (scxmls->getLength() == 0) {
+ ERROR_PLATFORM_THROW("Cannot find SCXML element in DOM");
+ return;
+ }
+
+ _scxml = dynamic_cast<DOMElement*>(scxmls->item(0));
+
+ _xmlPrefix = _scxml->getPrefix();
+ _xmlNS = _scxml->getNamespaceURI();
+ if (_xmlPrefix) {
+ _xmlPrefix = std::string(_xmlPrefix) + ":";
+ }
+ if (HAS_ATTR(_scxml, "name")) {
+ _name = ATTR(_scxml, "name");
+ } else {
+ _name = _baseURL.pathComponents().back();
+ }
+
+ _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY);
+
+ }
+
+}
+
+void InterpreterImpl::init() {
+
+ if (_isInitialized)
+ return;
+
+ setupDOM();
+
+ // register io processors
+ std::map<std::string, IOProcessorImpl*> ioProcs = _factory->getIOProcessors();
+ for (auto ioProcIter = ioProcs.begin(); ioProcIter != ioProcs.end(); ioProcIter++) {
+
+ // do not override if already set
+ if (_ioProcs.find(ioProcIter->first) != _ioProcs.end()) {
+ ioProcIter++;
+ continue;
+ }
+
+ // this might throw error.execution
+ _ioProcs[ioProcIter->first] = _factory->createIOProcessor(ioProcIter->first, this);
+
+ // register aliases
+ std::list<std::string> names = _ioProcs[ioProcIter->first].getNames();
+ for (auto nameIter = names.begin(); nameIter != names.end(); nameIter++) {
+ // do not override
+ if (!iequals(*nameIter, ioProcIter->first) && _ioProcs.find(*nameIter) == _ioProcs.end()) {
+ _ioProcs[*nameIter] = _ioProcs[ioProcIter->first];
+ }
+ }
+
+ }
+
+ if (!_microStepper) {
+ _microStepper = MicroStep(std::shared_ptr<MicroStepImpl>(new MicroStepFast(this)));
+ }
+ _microStepper.init(_scxml);
+
+ if (!_dataModel) {
+ _dataModel = _factory->createDataModel(HAS_ATTR(_scxml, "datamodel") ? ATTR(_scxml, "datamodel") : "null", this);
+ }
+ if (!_execContent) {
+ _execContent = ContentExecutor(std::shared_ptr<ContentExecutorImpl>(new BasicContentExecutorImpl(this)));
+ }
+
+ if (!_externalQueue) {
+ _externalQueue = EventQueue(std::shared_ptr<EventQueueImpl>(new EventQueueImpl()));
+ }
+ if (!_internalQueue) {
+ _internalQueue = EventQueue(std::shared_ptr<EventQueueImpl>(new EventQueueImpl()));
+ }
+ if (!_delayQueue) {
+ _delayQueue = DelayedEventQueue(std::shared_ptr<DelayedEventQueueImpl>(new DelayedEventQueueImpl(this)));
+ }
+
+ _isInitialized = true;
+}
+
+void InterpreterImpl::initData(xercesc::DOMElement* root) {
+ std::string id = ATTR(root, "id");
+ Data d;
+ try {
+ if (Event::getParam(_invokeReq.params, id, d)) {
+ _dataModel.init(id, d);
+ } else if (_invokeReq.namelist.find(id) != _invokeReq.namelist.end()) {
+ _dataModel.init(id, _invokeReq.namelist[id]);
+ } else {
+ _dataModel.init(id, _execContent.elementAsData(root));
+ }
+ } catch(ErrorEvent e) {
+ // test 277
+ enqueueInternal(e);
+ }
+}
+
+void InterpreterImpl::assign(const std::string& location, const Data& data) {
+ _dataModel.assign(location, data);
+}
+
+bool InterpreterImpl::isMatched(const Event& event, const std::string& eventDesc) {
+ return nameMatch(eventDesc, event.name);
+}
+
+bool InterpreterImpl::isTrue(const std::string& expr) {
+ try {
+ return _dataModel.evalAsBool(expr);
+ } catch (ErrorEvent e) {
+ // test 244: deliver error execution
+
+ LOG(ERROR) << e;
+
+ // test 344
+ enqueueInternal(e);
+ // test 245: undefined is false
+ return false;
+
+ }
+}
+
+
+bool InterpreterImpl::checkValidSendType(const std::string& type, const std::string& target) {
+
+ // this is the responsibility of the calling method
+ assert(type.size() > 0);
+
+ if (_ioProcs.find(type) == _ioProcs.end()) {
+ ERROR_EXECUTION_THROW("Type '" + type + "' not supported for sending");
+ }
+
+ if (!_ioProcs[type].isValidTarget(target)) {
+ ERROR_COMMUNICATION_THROW("Target '" + target + "' not supported in send");
+ }
+
+ return true;
+}
+
+Event InterpreterImpl::dequeueExternal(bool blocking) {
+ _currEvent = _externalQueue.dequeue(blocking);
+ if (_currEvent) {
+ _dataModel.setEvent(_currEvent);
+
+// LOG(ERROR) << e.name;
+
+ // test 233
+ if (_currEvent.invokeid.size() > 0 &&
+ _invokers.find(_currEvent.invokeid) != _invokers.end() &&
+ _invokers[_currEvent.invokeid].getFinalize() != NULL) {
+ _execContent.process(_invokers[_currEvent.invokeid].getFinalize(), _xmlPrefix);
+ }
+
+ for (auto invIter = _invokers.begin(); invIter != _invokers.end(); invIter++) {
+ // test 229
+ if (_autoForwarders.find(invIter->first) != _autoForwarders.end()) {
+ invIter->second.eventFromSCXML(_currEvent);
+ }
+ }
+ }
+ return _currEvent;
+}
+
+void InterpreterImpl::enqueue(const std::string& type, const std::string& target, size_t delayMs, const Event& sendEvent) {
+ std::lock_guard<std::recursive_mutex> lock(_delayMutex);
+
+ assert(sendEvent.uuid.length() > 0);
+ assert(_delayedEventTargets.find(sendEvent.uuid) == _delayedEventTargets.end());
+
+ _delayedEventTargets[sendEvent.uuid] = std::tuple<std::string, std::string, std::string>(sendEvent.sendid, type, target);
+ if (delayMs == 0) {
+ Event copy(sendEvent);
+ return eventReady(copy, sendEvent.uuid);
+ } else {
+ return _delayQueue.enqueueDelayed(sendEvent, delayMs, sendEvent.uuid);
+ }
+}
+
+void InterpreterImpl::cancelDelayed(const std::string& sendId) {
+ std::lock_guard<std::recursive_mutex> lock(_delayMutex);
+
+ // we need to find the uuids for the given sendid
+ for (auto evIter = _delayedEventTargets.begin(); evIter != _delayedEventTargets.end();) {
+ // inline deletion for maps: http://stackoverflow.com/a/263958/990120
+ if (std::get<0>(evIter->second) == sendId) {
+ _delayQueue.cancelDelayed(evIter->first);
+ evIter = _delayedEventTargets.erase(evIter);
+ } else {
+ evIter++;
+ }
+ }
+}
+
+void InterpreterImpl::eventReady(Event& sendEvent, const std::string& eventUUID) {
+ std::lock_guard<std::recursive_mutex> lock(_delayMutex);
+
+ // we only arrive here after the delay already passed!
+ assert(_delayedEventTargets.find(eventUUID) != _delayedEventTargets.end());
+
+ std::string type = std::get<1>(_delayedEventTargets[eventUUID]);
+ std::string target = std::get<2>(_delayedEventTargets[eventUUID]);
+
+ // test 172
+ if (type.size() == 0) {
+ type = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
+ }
+
+ _delayedEventTargets.erase(eventUUID);
+
+ if (_ioProcs.find(type) != _ioProcs.end()) {
+ _ioProcs[type].eventFromSCXML(target, sendEvent);
+ } else {
+ ERROR_PLATFORM_THROW("No IO processor " + type + " known");
+ }
+}
+
+void InterpreterImpl::invoke(const std::string& type, const std::string& src, bool autoForward, xercesc::DOMElement* finalize, const Event& invokeEvent) {
+
+ std::string tmp;
+ if (src.size() > 0) {
+ URL url(src);
+ if (!url.isAbsolute()) {
+ url = URL::resolve(url, _baseURL);
+ }
+ tmp = (std::string)url;
+ }
+
+ std::shared_ptr<InvokerImpl> invokerImpl = _factory->createInvoker(type, this);
+ invokerImpl->setFinalize(finalize);
+ _invokers[invokeEvent.invokeid] = invokerImpl;
+ _invokers[invokeEvent.invokeid].invoke(tmp, invokeEvent);
+
+ if (autoForward) {
+ _autoForwarders.insert(invokeEvent.invokeid);
+ }
+}
+
+void InterpreterImpl::uninvoke(const std::string& invokeId) {
+ if (_invokers.find(invokeId) != _invokers.end()) {
+ _invokers[invokeId].uninvoke();
+ _autoForwarders.erase(invokeId);
+ }
+
+}
+
+}
diff --git a/src/uscxml/interpreter/InterpreterImpl.h b/src/uscxml/interpreter/InterpreterImpl.h
new file mode 100644
index 0000000..7c64779
--- /dev/null
+++ b/src/uscxml/interpreter/InterpreterImpl.h
@@ -0,0 +1,290 @@
+/**
+ * @file
+ * @author 2016 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#ifndef INTERPRETERIMPL_H_29D5BEBA
+#define INTERPRETERIMPL_H_29D5BEBA
+
+#include <memory>
+#include <mutex>
+#include <list>
+#include <map>
+#include <string>
+
+#include "uscxml/Common.h"
+#include "uscxml/util/URL.h"
+#include "uscxml/plugins/Factory.h"
+#include "uscxml/plugins/DataModel.h"
+#include "uscxml/interpreter/MicroStepImpl.h"
+#include "uscxml/interpreter/ContentExecutorImpl.h"
+#include "uscxml/interpreter/EventQueueImpl.h"
+#include "uscxml/util/DOM.h"
+#include <xercesc/dom/DOM.hpp>
+
+namespace uscxml {
+
+class InterpreterMonitor;
+class InterpreterIssue;
+
+class USCXML_API ActionLanguage {
+public:
+ MicroStep microStepper;
+ DataModel dataModel;
+ ContentExecutor execContent;
+};
+
+class USCXML_API InterpreterImpl :
+ public MicroStepCallbacks,
+ public DataModelCallbacks,
+ public ContentExecutorCallbacks,
+ public DelayedEventQueueCallbacks
+// public std::enable_shared_from_this<InterpreterImpl>
+{
+public:
+ enum Binding {
+ EARLY = 0,
+ LATE = 1
+ };
+
+ InterpreterImpl();
+ virtual ~InterpreterImpl();
+
+ void cloneFrom(InterpreterImpl* other);
+ void cloneFrom(std::shared_ptr<InterpreterImpl> other);
+
+ virtual InterpreterState step(bool blocking) {
+ if (!_isInitialized) {
+ init();
+ _state = USCXML_INITIALIZED;
+ } else {
+ _state = _microStepper.step(blocking);
+ }
+ return _state;
+ }
+
+ virtual void reset() {///< Reset state machine
+ _microStepper.reset();
+ _isInitialized = false;
+ _state = USCXML_INSTANTIATED;
+// _dataModel.reset();
+// _eventQueue.reset();
+// _contentExecutor.reset();
+ }
+
+ virtual void cancel(); ///< Cancel and finalize state machine
+
+ InterpreterState getState() {
+ return _state;
+ }
+
+ std::list<xercesc::DOMElement*> getConfiguration() {
+ return _microStepper.getConfiguration();
+ }
+
+ void setMonitor(InterpreterMonitor* monitor) {
+ _monitor = monitor;
+ }
+
+ /**
+ MicrostepCallbacks
+ */
+ virtual Event dequeueInternal() {
+ _currEvent = _internalQueue.dequeue(false);
+ if (_currEvent)
+ _dataModel.setEvent(_currEvent);
+ return _currEvent;
+ }
+ virtual Event dequeueExternal(bool blocking);
+ virtual bool isTrue(const std::string& expr);
+
+ virtual void raiseDoneEvent(xercesc::DOMElement* state, xercesc::DOMElement* doneData) {
+ _execContent.raiseDoneEvent(state, doneData);
+ }
+
+ virtual void process(xercesc::DOMElement* block) {
+ _execContent.process(block, _xmlPrefix);
+ }
+
+ virtual bool isMatched(const Event& event, const std::string& eventDesc);
+ virtual void initData(xercesc::DOMElement* element);
+
+ virtual void invoke(xercesc::DOMElement* invoke) {
+ _execContent.invoke(invoke);
+ }
+
+ virtual void uninvoke(xercesc::DOMElement* invoke) {
+ _execContent.uninvoke(invoke);
+ }
+
+ virtual InterpreterMonitor* getMonitor() {
+ return _monitor;
+ }
+
+ /**
+ DataModelCallbacks
+ */
+ virtual const std::string& getName() {
+ return _name;
+ }
+ virtual const std::string& getSessionId() {
+ return _sessionId;
+ }
+ virtual const std::map<std::string, IOProcessor>& getIOProcessors() {
+ return _ioProcs;
+ }
+ virtual const std::map<std::string, Invoker>& getInvokers() {
+ return _invokers;
+ }
+
+ virtual bool isInState(const std::string& stateId) {
+ return _microStepper.isInState(stateId);
+ }
+ virtual xercesc::DOMDocument* getDocument() const {
+ return _document;
+ }
+
+ /**
+ ContentExecutorCallbacks
+ */
+
+ virtual void enqueueInternal(const Event& event) {
+ return _internalQueue.enqueue(event);
+ }
+ virtual void enqueueExternal(const Event& event) {
+ return _externalQueue.enqueue(event);
+ }
+ virtual void enqueueExternalDelayed(const Event& event, size_t delayMs, const std::string& eventUUID) {
+ return _delayQueue.enqueueDelayed(event, delayMs, eventUUID);
+ }
+ virtual void cancelDelayed(const std::string& eventId);
+
+ virtual size_t getLength(const std::string& expr) {
+ return _dataModel.getLength(expr);
+ }
+
+ virtual void setForeach(const std::string& item,
+ const std::string& array,
+ const std::string& index,
+ uint32_t iteration) {
+ return _dataModel.setForeach(item, array, index, iteration);
+ }
+ virtual Data evalAsData(const std::string& expr) {
+ return _dataModel.evalAsData(expr);
+ }
+
+ virtual Data getAsData(const std::string& expr) {
+ return _dataModel.getAsData(expr);
+ }
+
+ virtual void assign(const std::string& location, const Data& data);
+
+ virtual std::string getInvokeId() {
+ return _invokeId;
+ }
+ virtual std::string getBaseURL() {
+ return _baseURL;
+ }
+
+ virtual bool checkValidSendType(const std::string& type, const std::string& target);
+ virtual void invoke(const std::string& type, const std::string& src, bool autoForward, xercesc::DOMElement* finalize, const Event& invokeEvent);
+ virtual void uninvoke(const std::string& invokeId);
+ virtual void enqueue(const std::string& type, const std::string& target, size_t delayMs, const Event& sendEvent);
+
+ virtual const Event& getCurrentEvent() {
+ return _currEvent;
+ }
+
+ /**
+ DelayedEventQueueCallbacks
+ */
+
+ virtual void eventReady(Event& event, const std::string& eventUUID);
+
+ /** --- */
+
+ void setActionLanguage(const ActionLanguage& al) {
+ _execContent = al.execContent;
+ _microStepper = al.microStepper;
+ _dataModel = al.dataModel;
+ }
+
+ static std::map<std::string, std::weak_ptr<InterpreterImpl> > getInstances();
+
+ virtual xercesc::DOMDocument* getDocument() {
+ return _document;
+ }
+
+protected:
+ static void addInstance(std::shared_ptr<InterpreterImpl> instance);
+
+ Binding _binding;
+
+ std::string _sessionId;
+ std::string _name;
+ std::string _invokeId; // TODO: Never set!
+
+ bool _isInitialized;
+ xercesc::DOMDocument* _document;
+ xercesc::DOMElement* _scxml;
+
+ std::map<std::string, std::tuple<std::string, std::string, std::string> > _delayedEventTargets;
+
+ virtual void init();
+
+ static std::map<std::string, std::weak_ptr<InterpreterImpl> > _instances;
+ static std::recursive_mutex _instanceMutex;
+ std::recursive_mutex _delayMutex;
+
+ friend class Interpreter;
+ friend class InterpreterIssue;
+ friend class TransformerImpl;
+ friend class USCXMLInvoker;
+ friend class SCXMLIOProcessor;
+
+ X _xmlPrefix;
+ X _xmlNS;
+ Factory* _factory;
+
+ URL _baseURL;
+
+ MicroStep _microStepper;
+ DataModel _dataModel;
+ ContentExecutor _execContent;
+
+ InterpreterState _state;
+
+ EventQueue _internalQueue;
+ EventQueue _externalQueue;
+ EventQueue _parentQueue;
+ DelayedEventQueue _delayQueue;
+
+ Event _currEvent;
+ Event _invokeReq;
+
+ std::map<std::string, IOProcessor> _ioProcs;
+ std::map<std::string, Invoker> _invokers;
+ std::set<std::string> _autoForwarders;
+ InterpreterMonitor* _monitor;
+
+private:
+ void setupDOM();
+};
+
+}
+
+#endif /* end of include guard: INTERPRETERIMPL_H_29D5BEBA */
diff --git a/src/uscxml/interpreter/InterpreterMonitor.h b/src/uscxml/interpreter/InterpreterMonitor.h
new file mode 100644
index 0000000..6ebdb35
--- /dev/null
+++ b/src/uscxml/interpreter/InterpreterMonitor.h
@@ -0,0 +1,95 @@
+/**
+ * @file
+ * @author 2016 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#ifndef INTERPRETERMONITOR_H_4BA77097
+#define INTERPRETERMONITOR_H_4BA77097
+
+#include "uscxml/Common.h"
+#include "uscxml/messages/Event.h"
+#include "uscxml/debug/InterpreterIssue.h"
+
+#include <mutex>
+
+#define USCXML_MONITOR_CATCH(callback) \
+catch (Event e) { LOG(ERROR) << "Syntax error when calling " #callback " on monitors: " << std::endl << e << std::endl; } \
+catch (std::bad_weak_ptr e) { LOG(ERROR) << "Unclean shutdown " << std::endl; } \
+catch (...) { LOG(ERROR) << "An exception occurred when calling " #callback " on monitors"; } \
+if (_state == USCXML_DESTROYED) { throw std::bad_weak_ptr(); }
+
+#define USCXML_MONITOR_CALLBACK(callback, function) \
+if (callback) { callback->function(); }
+
+#define USCXML_MONITOR_CALLBACK1(callback, function, arg1) \
+if (callback) { callback->function(arg1); }
+
+#define USCXML_MONITOR_CALLBACK2(callback, function, arg1, arg2) \
+if (callback) { callback->function(arg1, arg2); }
+
+namespace uscxml {
+
+class USCXML_API InterpreterMonitor {
+public:
+ InterpreterMonitor() : _copyToInvokers(false) {}
+ virtual ~InterpreterMonitor() {}
+
+ virtual void beforeProcessingEvent(const Event& event) {}
+ virtual void beforeMicroStep() {}
+
+ virtual void beforeExitingState(const xercesc::DOMElement* state) {}
+ virtual void afterExitingState(const xercesc::DOMElement* state) {}
+
+ virtual void beforeExecutingContent(const xercesc::DOMElement* execContent) {}
+ virtual void afterExecutingContent(const xercesc::DOMElement* execContent) {}
+
+ virtual void beforeUninvoking(const xercesc::DOMElement* invokeElem, const std::string& invokeid) {}
+ virtual void afterUninvoking(const xercesc::DOMElement* invokeElem, const std::string& invokeid) {}
+
+ virtual void beforeTakingTransition(const xercesc::DOMElement* transition) {}
+ virtual void afterTakingTransition(const xercesc::DOMElement* transition) {}
+
+ virtual void beforeEnteringState(const xercesc::DOMElement* state) {}
+ virtual void afterEnteringState(const xercesc::DOMElement* state) {}
+
+ virtual void beforeInvoking(const xercesc::DOMElement* invokeElem, const std::string& invokeid) {}
+ virtual void afterInvoking(const xercesc::DOMElement* invokeElem, const std::string& invokeid) {}
+
+ virtual void afterMicroStep() {}
+ virtual void onStableConfiguration() {}
+
+ virtual void beforeCompletion() {}
+ virtual void afterCompletion() {}
+
+ virtual void reportIssue(const InterpreterIssue& issue) {}
+
+ void copyToInvokers(bool copy) {
+ _copyToInvokers = copy;
+ }
+
+ bool copyToInvokers() {
+ return _copyToInvokers;
+ }
+
+protected:
+ bool _copyToInvokers;
+
+};
+
+}
+
+#endif /* end of include guard: INTERPRETERMONITOR_H_4BA77097 */
diff --git a/src/uscxml/interpreter/InterpreterRC.cpp b/src/uscxml/interpreter/InterpreterRC.cpp
deleted file mode 100644
index b594c18..0000000
--- a/src/uscxml/interpreter/InterpreterRC.cpp
+++ /dev/null
@@ -1,661 +0,0 @@
-/**
- * @file
- * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
- * @copyright Simplified BSD
- *
- * @cond
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the FreeBSD license as published by the FreeBSD
- * project.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * You should have received a copy of the FreeBSD license along with this
- * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
- * @endcond
- */
-
-#include "InterpreterRC.h"
-
-#include "uscxml/Factory.h"
-#include "uscxml/concurrency/DelayedEventQueue.h"
-
-#include <glog/logging.h>
-#include "uscxml/UUID.h"
-#include "uscxml/dom/DOMUtils.h"
-
-#define VERBOSE 0
-#define VERBOSE_STATE_SELECTION 0
-#define VERBOSE_FIND_LCCA 0
-
-namespace uscxml {
-
-using namespace Arabica::XPath;
-using namespace Arabica::DOM;
-
-#if 1
-size_t padding = 0;
-std::string getPadding() {
- std::string pad = "";
- for (size_t i = 0; i < padding; i++) {
- pad += " ";
- }
- return pad;
-}
-#endif
-
-Arabica::XPath::NodeSet<std::string> InterpreterRC::removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- Arabica::XPath::NodeSet<std::string> filteredTransitions;
- for (unsigned int i = 0; i < enabledTransitions.size(); i++) {
- Element<std::string> t1(enabledTransitions[i]);
- bool t1Preempted = false;
- Arabica::XPath::NodeSet<std::string> transitionsToRemove;
-
- for (unsigned int j = 0; j < filteredTransitions.size(); j++) {
- Element<std::string> t2(filteredTransitions[j]);
- if (hasIntersection(computeExitSet(t1), computeExitSet(t2))) {
- if (isDescendant(getSourceState(t1), getSourceState(t2))) {
- transitionsToRemove.push_back(t2);
- } else {
- t1Preempted = true;
- break;
- }
- }
- }
-
- if (!t1Preempted) {
- // remove transitionsToRemove from DOMUtils::filteredTransitions
- std::list<Node<std::string> > tmp;
- for (size_t i = 0; i < filteredTransitions.size(); i++) {
- if (!isMember(filteredTransitions[i], transitionsToRemove)) {
- tmp.push_back(filteredTransitions[i]);
- }
- }
- filteredTransitions = NodeSet<std::string>();
- filteredTransitions.insert(filteredTransitions.end(), tmp.begin(), tmp.end());
-
- filteredTransitions.push_back(t1);
- }
- }
- return filteredTransitions;
-}
-
-bool InterpreterRC::hasIntersection(const Arabica::XPath::NodeSet<std::string>& nodeSet1, const Arabica::XPath::NodeSet<std::string>& nodeSet2) {
- for (unsigned int i = 0; i < nodeSet1.size(); i++) {
- for (unsigned int j = 0; j < nodeSet2.size(); j++) {
- if (nodeSet1[i] == nodeSet2[j])
- return true;
- }
- }
- return false;
-}
-
-
-void InterpreterRC::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- NodeSet<std::string> statesToExit = computeExitSet(enabledTransitions);
-
-#if VERBOSE_STATE_SELECTION
- std::cout << "exitStates: ";
- for (size_t i = 0; i < statesToExit.size(); i++) {
- std::cout << ATTR_CAST(statesToExit[i], "id") << " ";
- }
- std::cout << std::endl;
-#endif
-
- // remove statesToExit from _statesToInvoke
- std::list<Node<std::string> > tmp;
- for (size_t i = 0; i < _statesToInvoke.size(); i++) {
- if (!isMember(_statesToInvoke[i], statesToExit)) {
- tmp.push_back(_statesToInvoke[i]);
- }
- }
- _statesToInvoke = NodeSet<std::string>();
- _statesToInvoke.insert(_statesToInvoke.end(), tmp.begin(), tmp.end());
-
- statesToExit.forward(false);
- statesToExit.sort();
-
-
- for (size_t i = 0; i < statesToExit.size(); i++) {
- NodeSet<std::string> histories = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "history", statesToExit[i]);
- for (size_t j = 0; j < histories.size(); j++) {
- Element<std::string> historyElem = (Element<std::string>)histories[j];
- std::string historyType = (historyElem.hasAttribute("type") ? historyElem.getAttribute("type") : "shallow");
- NodeSet<std::string> historyNodes;
- for (size_t k = 0; k < _configuration.size(); k++) {
- if (iequals(historyType, "deep")) {
- if (isAtomic(Element<std::string>(_configuration[k])) && isDescendant(_configuration[k], statesToExit[i]))
- historyNodes.push_back(_configuration[k]);
- } else {
- if (_configuration[k].getParentNode() == statesToExit[i])
- historyNodes.push_back(_configuration[k]);
- }
- }
- _historyValue[historyElem.getAttribute("id")] = historyNodes;
- }
- }
-
- for (size_t i = 0; i < statesToExit.size(); i++) {
- USCXML_MONITOR_CALLBACK3(beforeExitingState, Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size()))
-
- NodeSet<std::string> onExits = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "onExit", statesToExit[i]);
- for (size_t j = 0; j < onExits.size(); j++) {
- Element<std::string> onExitElem = (Element<std::string>)onExits[j];
- executeContent(onExitElem);
- }
-
- USCXML_MONITOR_CALLBACK3(afterExitingState, Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size()))
-
- NodeSet<std::string> invokes = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]);
- for (size_t j = 0; j < invokes.size(); j++) {
- Element<std::string> invokeElem = (Element<std::string>)invokes[j];
- if (HAS_ATTR(invokeElem, "persist") && stringIsTrue(ATTR(invokeElem, "persist"))) {
- } else {
- cancelInvoke(invokeElem);
- }
- }
-
- // remove statesToExit[i] from _configuration - test409
- tmp.clear();
- for (size_t j = 0; j < _configuration.size(); j++) {
- if (_configuration[j] != statesToExit[i]) {
- tmp.push_back(_configuration[j]);
- }
- }
- _configuration = NodeSet<std::string>();
- _configuration.insert(_configuration.end(), tmp.begin(), tmp.end());
- }
-}
-
-/*
-function computeExitSet(transitions)
- statesToExit = new OrderedSet
- for t in transitions:
- if(t.target):
- domain = getTransitionDomain(t)
- for s in configuration:
- if isDescendant(s,domain):
- statesToExit.add(s)
- return statesToExit
-*/
-Arabica::XPath::NodeSet<std::string> InterpreterRC::computeExitSet(const Arabica::XPath::NodeSet<std::string>& transitions) {
-
- NodeSet<std::string> statesToExit;
- for (unsigned int i = 0; i < transitions.size(); i++) {
- Element<std::string> t(transitions[i]);
- if (!isTargetless(t)) {
- Arabica::DOM::Node<std::string> domain = getTransitionDomain(t);
- if (!domain)
- continue;
- for (unsigned int j = 0; j < _configuration.size(); j++) {
- const Node<std::string>& s = _configuration[j];
- if (isDescendant(s, domain)) {
- statesToExit.push_back(s);
- }
- }
- }
- }
-#if VERBOSE
- std::cout << "computeExitSet: ";
- for (size_t i = 0; i < statesToExit.size(); i++) {
- std::cout << ATTR_CAST(statesToExit[i], "id") << " ";
- }
- std::cout << std::endl;
-#endif
-
- return statesToExit;
-}
-
-Arabica::XPath::NodeSet<std::string> InterpreterRC::computeExitSet(const Arabica::DOM::Node<std::string>& transition) {
-// if (_exitSet.find(transition) != _exitSet.end()) // speed up removeConflicting
-// return _exitSet[transition];
-
- Arabica::XPath::NodeSet<std::string> transitions;
- transitions.push_back(transition);
-
- Arabica::XPath::NodeSet<std::string> exitSet = computeExitSet(transitions);
- //_exitSet[transition] = exitSet;
-
-#if 0
- std::cerr << "Exit set for transition '" << transition << "': " << std::endl;
- for (size_t i = 0; i < exitSet.size(); i++) {
- std::cerr << ATTR_CAST(exitSet[i], "id") << std::endl << "----" << std::endl;
- }
- std::cerr << std::endl;
-#endif
- return exitSet;
-}
-
-void InterpreterRC::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- NodeSet<std::string> statesToEnter;
- NodeSet<std::string> statesForDefaultEntry;
- // initialize the temporary table for default content in history states
- std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent;
-
- computeEntrySet(enabledTransitions, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- statesToEnter.to_document_order();
-
-#if VERBOSE_STATE_SELECTION
- std::cout << "enterStates: ";
- for (size_t i = 0; i < statesToEnter.size(); i++) {
- std::cout << ATTR_CAST(statesToEnter[i], "id") << " ";
- }
- std::cout << std::endl;
-#endif
-
- for (size_t i = 0; i < statesToEnter.size(); i++) {
- Element<std::string> s = (Element<std::string>)statesToEnter[i];
-
- USCXML_MONITOR_CALLBACK3(beforeEnteringState, s, i + 1 < statesToEnter.size())
-
- _configuration.push_back(s);
- _statesToInvoke.push_back(s);
-
- if (_binding == LATE && !isMember(s, _alreadyEntered)) {
- NodeSet<std::string> dataModelElems = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "datamodel", s);
- if(dataModelElems.size() > 0 && _dataModel) {
- Arabica::XPath::NodeSet<std::string> dataElems = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "data", dataModelElems[0]);
- for (size_t j = 0; j < dataElems.size(); j++) {
- if (dataElems[j].getNodeType() == Node_base::ELEMENT_NODE)
- initializeData(Element<std::string>(dataElems[j]));
- }
- }
- _alreadyEntered.push_back(s);
- }
- // execute onentry executable content
- NodeSet<std::string> onEntryElems = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "onEntry", s);
- executeContent(onEntryElems, false);
-
- if (isMember(s, statesForDefaultEntry)) {
- // execute initial transition content for compound states
- Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", s).asNodeSet();
- if (transitions.size() > 0) {
- executeContent(transitions);
- }
- }
-
- USCXML_MONITOR_CALLBACK3(afterEnteringState, s, i + 1 < statesToEnter.size())
-
- if (HAS_ATTR(_scxml, "flat") && stringIsTrue(ATTR(_scxml, "flat"))) {
- // extension for flattened interpreters
- NodeSet<std::string> invokes = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "invoke", s);
- for (unsigned int j = 0; j < invokes.size(); j++) {
- Element<std::string> invokeElem = Element<std::string>(invokes[j]);
- if (HAS_ATTR(invokeElem, "persist") && stringIsTrue(ATTR(invokeElem, "persist"))) {
- invoke(invokeElem);
- }
- }
-
- // extension for flattened SCXML documents, we will need an explicit uninvoke element
- NodeSet<std::string> uninvokes = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "uninvoke", s);
- for (size_t j = 0; j < uninvokes.size(); j++) {
- Element<std::string> uninvokeElem = (Element<std::string>)uninvokes[j];
- cancelInvoke(uninvokeElem);
- }
- }
-
-// std::cout << "HIST?: " << ATTR(s, "id") << std::endl;
- if (defaultHistoryContent.find(ATTR(s, "id")) != defaultHistoryContent.end()) {
- executeContent(Element<std::string>(defaultHistoryContent[ATTR(s, "id")]));
- }
-
- if (isFinal(s)) {
- Element<std::string> parent = (Element<std::string>)s.getParentNode();
-
- Arabica::DOM::Element<std::string> doneData;
- Arabica::XPath::NodeSet<std::string> doneDatas = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "donedata", s);
- if (doneDatas.size() > 0) {
- // only process first donedata element
- doneData = Element<std::string>(doneDatas[0]);
- }
-
- if (parentIsScxmlState(s)) {
- _topLevelFinalReached = true;
- } else {
- internalDoneSend(parent, doneData);
- Element<std::string> grandParent = (Element<std::string>)parent.getParentNode();
-
-// internalDoneSend(parent, Arabica::DOM::Element<std::string>());
-
- if (isParallel(grandParent)) {
- Arabica::XPath::NodeSet<std::string> childs = getChildStates(grandParent);
- bool inFinalState = true;
- for (size_t j = 0; j < childs.size(); j++) {
- if (!isInFinalState(Element<std::string>(childs[j]))) {
- inFinalState = false;
- break;
- }
- }
- if (inFinalState) {
- internalDoneSend(grandParent, Arabica::DOM::Element<std::string>());
- }
- }
- }
- }
- }
-}
-
-/*
-procedure computeEntrySet(transitions, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
- for t in transitions:
- for s in t.target:
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
- ancestor = getTransitionDomain(t)
- for s in getEffectiveTargetStates(t)):
- addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
-*/
-
-void InterpreterRC::computeEntrySet(const Arabica::XPath::NodeSet<std::string>& transitions,
- NodeSet<std::string>& statesToEnter,
- NodeSet<std::string>& statesForDefaultEntry,
- std::map<std::string, Arabica::DOM::Node<std::string> >& defaultHistoryContent) {
-
- // add all descendants in a dedicated first step
- for (size_t i = 0; i < transitions.size(); i++) {
- Element<std::string> t(transitions[i]);
-
- NodeSet<std::string> targets = getTargetStates(t);
- for (size_t j = 0; j < targets.size(); j++) {
- Element<std::string> s = Element<std::string>(targets[j]);
- addDescendantStatesToEnter(s, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
- }
-
- // only now add the ancestors
- for (size_t i = 0; i < transitions.size(); i++) {
- Element<std::string> t(transitions[i]);
- Element<std::string> ancestor = Element<std::string>(getTransitionDomain(t));
- NodeSet<std::string> effectiveTargetStates = getEffectiveTargetStates(t);
-
- for (size_t j = 0; j < effectiveTargetStates.size(); j++) {
- Element<std::string> s = Element<std::string>(effectiveTargetStates[j]);
- addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
-
- }
-}
-
-/*
-function getEffectiveTargetStates(transition)
- effectiveTargets = new OrderedSet()
- for s in transition.target
- if isHistoryState(s):
- if historyValue[s.id]:
- effectiveTargets.union(historyValue[s.id])
- else:
- effectiveTargets.union(getEffectiveTargetStates(s.transition))
- else:
- effectiveTargets.add(s)
- return effectiveTargets
-*/
-
-Arabica::XPath::NodeSet<std::string> InterpreterRC::getEffectiveTargetStates(const Arabica::DOM::Element<std::string>& transition) {
- NodeSet<std::string> effectiveTargets;
-
- NodeSet<std::string> targets;
- if (isState(transition)) {
- targets = getInitialStates(transition);
- return targets;
- } else {
- targets = getTargetStates(transition);
- }
-
- for (size_t j = 0; j < targets.size(); j++) {
- Element<std::string> s = Element<std::string>(targets[j]);
- if (isHistory(s)) {
- if (_historyValue.find(ATTR(s, "id")) != _historyValue.end()) {
- targets.push_back(_historyValue[ATTR(s, "id")]);
- } else {
- NodeSet<std::string> histTrans = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "transition", s);
- // TODO: what if there are many history transitions?
- if (histTrans.size() > 0)
- targets.push_back(getEffectiveTargetStates(Element<std::string>(histTrans[0])));
- }
- } else {
- effectiveTargets.push_back(s);
- }
- }
-
- return effectiveTargets;
-}
-
-void InterpreterRC::computeEntrySet(const Arabica::DOM::Node<std::string>& transition,
- NodeSet<std::string>& statesToEnter,
- NodeSet<std::string>& statesForDefaultEntry,
- std::map<std::string, Arabica::DOM::Node<std::string> >& defaultHistoryContent) {
- Arabica::XPath::NodeSet<std::string> transitions;
- transitions.push_back(transition);
- computeEntrySet(transitions, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
-}
-
-
-/*
-procedure addDescendantStatesToEnter(state,statesToEnter,statesForDefaultEntry, defaultHistoryContent):
- if isHistoryState(state):
- if historyValue[state.id]:
- for s in historyValue[state.id]:
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
- addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
- else:
- defaultHistoryContent[state.parent.id] = state.transition.content
- for s in state.transition.target:
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
- addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
- else:
- statesToEnter.add(state)
- if isCompoundState(state):
- statesForDefaultEntry.add(state)
- for s in getEffectiveTargetStates(state.initial.transition):
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
- addAncestorStatesToEnter(s, state, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
- else:
- if isParallelState(state):
- for child in getChildStates(state):
- if not statesToEnter.some(lambda s: isDescendant(s,child)):
- addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
-*/
-void InterpreterRC::addDescendantStatesToEnter(const Arabica::DOM::Element<std::string>& state,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
- std::map<std::string, Arabica::DOM::Node<std::string> >& defaultHistoryContent) {
-
-#if VERBOSE_STATE_SELECTION
- std::cout << getPadding() << "addDescendantStatesToEnter: " << ATTR(state, "id") << std::endl;
- padding++;
-#endif
-
- if (isHistory(state)) {
-
- /*
- if historyValue[state.id]:
- for s in historyValue[state.id]:
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
- addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
- else:
- defaultHistoryContent[state.parent.id] = state.transition.content
- for s in state.transition.target:
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
- addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
- */
- std::string stateId = ATTR(state, "id");
- if (_historyValue.find(stateId) != _historyValue.end()) {
- const Arabica::XPath::NodeSet<std::string>& historyValue = _historyValue[stateId];
- for (size_t i = 0; i < historyValue.size(); i++) {
- const Element<std::string>& s = Element<std::string>(historyValue[i]);
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
- addAncestorStatesToEnter(s, getParentState(s), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
-
- } else {
- NodeSet<std::string> transitions = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "transition", state);
- if (transitions.size() > 0) {
- // TODO: what if there are many history transitions?
-// std::cout << "HIST: " << ATTR_CAST(getParentState(state), "id") << std::endl;
- defaultHistoryContent[ATTR_CAST(getParentState(state), "id")] = transitions[0];
- }
-
- for (size_t i = 0; i < transitions.size(); i++) {
- NodeSet<std::string> targets = getTargetStates(Element<std::string>(transitions[i]));
- for (size_t j = 0; j < targets.size(); j++) {
- const Element<std::string>& s = Element<std::string>(targets[i]);
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
- addAncestorStatesToEnter(s, getParentState(s), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
- }
- }
- } else {
- /*
- statesToEnter.add(state)
- if isCompoundState(state):
- statesForDefaultEntry.add(state)
- for s in getEffectiveTargetStates(state.initial.transition):
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
- addAncestorStatesToEnter(s, state, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
- else:
- if isParallelState(state):
- for child in getChildStates(state):
- if not statesToEnter.some(lambda s: isDescendant(s,child)):
- addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
- */
-
- if (!isMember(state, statesToEnter)) { // adding an existing element invalidates old reference
-#if VERBOSE_STATE_SELECTION
- std::cout << getPadding() << "adding: " << ATTR_CAST(state, "id") << std::endl;
-#endif
- statesToEnter.push_back(state);
- }
-
- if (isCompound(state)) {
- statesForDefaultEntry.push_back(state);
-
- // test 579 - initial leads to history still fails
- NodeSet<std::string> targets = getEffectiveTargetStates(Element<std::string>(state));
- for (size_t i = 0; i < targets.size(); i++) {
- const Element<std::string>& s = Element<std::string>(targets[i]);
- addDescendantStatesToEnter(s, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
-
- for (size_t i = 0; i < targets.size(); i++) {
- const Element<std::string>& s = Element<std::string>(targets[i]);
- addAncestorStatesToEnter(s, state, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
-
- } else if(isParallel(state)) {
- NodeSet<std::string> childStates = getChildStates(state);
-
- for (size_t i = 0; i < childStates.size(); i++) {
- const Element<std::string>& child = Element<std::string>(childStates[i]);
-
- for (size_t j = 0; j < statesToEnter.size(); j++) {
- const Node<std::string>& s = statesToEnter[j];
- if (isDescendant(s, child)) {
- goto BREAK_LOOP;
- }
-
- }
- addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
-BREAK_LOOP:
- ;
- }
- }
- }
- padding--;
-}
-
-/*
-procedure addAncestorStatesToEnter(state, ancestor, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
- for anc in getProperAncestors(state,ancestor):
- statesToEnter.add(anc)
- if isParallelState(anc):
- for child in getChildStates(anc):
- if not statesToEnter.some(lambda s: isDescendant(s,child)):
- addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
-*/
-void InterpreterRC::addAncestorStatesToEnter(const Arabica::DOM::Element<std::string>& state,
- const Arabica::DOM::Element<std::string>& ancestor,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
- std::map<std::string, Arabica::DOM::Node<std::string> >& defaultHistoryContent) {
-
-#if VERBOSE_STATE_SELECTION
- std::cout << getPadding() << "addAncestorStatesToEnter: " << ATTR(state, "id") << " - " << ATTR(ancestor, "id") << std::endl;
- padding++;
-#endif
-
- NodeSet<std::string> ancestors = getProperAncestors(state, ancestor);
- for (size_t i = 0; i < ancestors.size(); i++) {
- const Node<std::string>& anc = ancestors[i];
-#if VERBOSE_STATE_SELECTION
- std::cout << getPadding() << "adding: " << ATTR_CAST(anc, "id") << std::endl;
-#endif
-
- statesToEnter.push_back(anc);
-
- if (isParallel(Element<std::string>(anc))) {
- NodeSet<std::string> childStates = getChildStates(anc);
- for (size_t j = 0; j < childStates.size(); j++) {
- const Element<std::string>& child = Element<std::string>(childStates[j]);
- for (size_t k = 0; k < statesToEnter.size(); k++) {
- const Node<std::string>& s = statesToEnter[k];
- if (isDescendant(s, child)) {
- goto BREAK_LOOP;
- }
- }
- addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
-BREAK_LOOP:
- ;
- }
- }
- }
- padding--;
-}
-
-/*
-function getTransitionDomain(t)
- tstates = getEffectiveTargetStates(t)
- if not tstates:
- return null
- elif t.type == "internal" and isCompoundState(t.source) and tstates.every(lambda s: isDescendant(s,t.source)):
- return t.source
- else:
- return findLCCA([t.source].append(tstates))
-*/
-Arabica::DOM::Node<std::string> InterpreterRC::getTransitionDomain(const Arabica::DOM::Element<std::string>& transition) {
- NodeSet<std::string> tStates = getEffectiveTargetStates(transition);
- if (tStates.size() == 0) {
- return Arabica::DOM::Node<std::string>(); // null
- }
- std::string transitionType = (HAS_ATTR(transition, "type") ? ATTR(transition, "type") : "external");
- Node<std::string> source = getSourceState(transition);
-
- if (iequals(transitionType, "internal") && isCompound(Element<std::string>(source))) {
- for (size_t i = 0; i < tStates.size(); i++) {
- const Node<std::string>& s = tStates[i];
- if (!isDescendant(s, source))
- goto BREAK_LOOP;
- }
- return source;
- }
-
-BREAK_LOOP:
- Arabica::XPath::NodeSet<std::string> states;
- states.push_back(source);
- states.push_back(tStates);
- return findLCCA(states);
-}
-
-void InterpreterRC::handleDOMEvent(Arabica::DOM::Events::Event<std::string>& event) {
- InterpreterImpl::handleDOMEvent(event);
-
- if (event.getType().compare("DOMAttrModified") == 0) // we do not care about attributes
- return;
-
-// Node<std::string> target = Arabica::DOM::Node<std::string>(event.getTarget());
-// NodeSet<std::string> transitions = DOMUtils::DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "transition", target, true);
-// if (transitions.size() > 0)
- _exitSet.clear();
-
-}
-} \ No newline at end of file
diff --git a/src/uscxml/interpreter/InterpreterRC.h b/src/uscxml/interpreter/InterpreterRC.h
deleted file mode 100644
index 36aca3d..0000000
--- a/src/uscxml/interpreter/InterpreterRC.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * @file
- * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
- * @copyright Simplified BSD
- *
- * @cond
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the FreeBSD license as published by the FreeBSD
- * project.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * You should have received a copy of the FreeBSD license along with this
- * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
- * @endcond
- */
-
-#ifndef INTERPRETERRC_H_WLJEI019
-#define INTERPRETERRC_H_WLJEI019
-
-#include "uscxml/Interpreter.h"
-
-namespace uscxml {
-
-class InterpreterRC : public InterpreterImpl {
-protected:
- void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
- void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
- Arabica::XPath::NodeSet<std::string> removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
-
- bool hasIntersection(const Arabica::XPath::NodeSet<std::string>& nodeSet1, const Arabica::XPath::NodeSet<std::string>& nodeSet2);
-
- Arabica::XPath::NodeSet<std::string> computeExitSet(const Arabica::XPath::NodeSet<std::string>& transitions);
- Arabica::XPath::NodeSet<std::string> computeExitSet(const Arabica::DOM::Node<std::string>& transition);
-
- void computeEntrySet(const Arabica::XPath::NodeSet<std::string>& transitions,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
- std::map<std::string, Arabica::DOM::Node<std::string> >& defaultHistoryContent);
- void computeEntrySet(const Arabica::DOM::Node<std::string>& transition,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
- std::map<std::string, Arabica::DOM::Node<std::string> >& defaultHistoryContent);
-
- Arabica::DOM::Node<std::string> getTransitionDomain(const Arabica::DOM::Element<std::string>& transition);
- Arabica::XPath::NodeSet<std::string> getEffectiveTargetStates(const Arabica::DOM::Element<std::string>& transition);
-
- void addDescendantStatesToEnter(const Arabica::DOM::Element<std::string>& state,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
- std::map<std::string, Arabica::DOM::Node<std::string> >& defaultHistoryContent);
-
- void addAncestorStatesToEnter(const Arabica::DOM::Element<std::string>& state,
- const Arabica::DOM::Element<std::string>& ancestor,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
- std::map<std::string, Arabica::DOM::Node<std::string> >& defaultHistoryContent);
- virtual void handleDOMEvent(Arabica::DOM::Events::Event<std::string>& event);
-
-private:
- std::map<Arabica::DOM::Node<std::string>, Arabica::XPath::NodeSet<std::string> > _exitSet;
-};
-
-}
-
-#endif /* end of include guard: INTERPRETERRC_H_WLJEI019 */
diff --git a/src/uscxml/interpreter/MicroStepFast.cpp b/src/uscxml/interpreter/MicroStepFast.cpp
new file mode 100644
index 0000000..fbddbc9
--- /dev/null
+++ b/src/uscxml/interpreter/MicroStepFast.cpp
@@ -0,0 +1,1149 @@
+/**
+ * @file
+ * @author 2012-2016 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#undef USCXML_VERBOSE
+
+#include "MicroStepFast.h"
+#include "uscxml/util/DOM.h"
+#include "uscxml/util/String.h"
+#include "uscxml/util/Predicates.h"
+#include "uscxml/util/Convenience.h"
+#include "uscxml/interpreter/InterpreterMonitor.h"
+
+#include <easylogging++.h>
+
+#define BIT_ANY_SET(b) (!b.none())
+#define BIT_HAS(idx, bitset) (bitset[idx])
+#define BIT_HAS_AND(bitset1, bitset2) bitset1.intersects(bitset2)
+#define BIT_SET_AT(idx, bitset) bitset[idx] = true;
+#define BIT_CLEAR(idx, bitset) bitset[idx] = false;
+
+#define USCXML_GET_TRANS(i) (*_transitions[i])
+#define USCXML_GET_STATE(i) (*_states[i])
+
+#define USCXML_CTX_PRISTINE 0x00
+#define USCXML_CTX_SPONTANEOUS 0x01
+#define USCXML_CTX_INITIALIZED 0x02
+#define USCXML_CTX_TOP_LEVEL_FINAL 0x04
+#define USCXML_CTX_TRANSITION_FOUND 0x08
+#define USCXML_CTX_FINISHED 0x10
+#define USCXML_CTX_STABLE 0x20 // only needed to signal onStable once
+
+#define USCXML_TRANS_SPONTANEOUS 0x01
+#define USCXML_TRANS_TARGETLESS 0x02
+#define USCXML_TRANS_INTERNAL 0x04
+#define USCXML_TRANS_HISTORY 0x08
+#define USCXML_TRANS_INITIAL 0x10
+
+#define USCXML_STATE_ATOMIC 0x01
+#define USCXML_STATE_PARALLEL 0x02
+#define USCXML_STATE_COMPOUND 0x03
+#define USCXML_STATE_FINAL 0x04
+#define USCXML_STATE_HISTORY_DEEP 0x05
+#define USCXML_STATE_HISTORY_SHALLOW 0x06
+#define USCXML_STATE_INITIAL 0x07
+#define USCXML_STATE_HAS_HISTORY 0x80 /* highest bit */
+#define USCXML_STATE_MASK(t) (t & 0x7F) /* mask highest bit */
+
+#define USCXML_NUMBER_STATES _states.size()
+#define USCXML_NUMBER_TRANS _transitions.size()
+
+#ifdef __GNUC__
+# define likely(x) (__builtin_expect(!!(x), 1))
+# define unlikely(x) (__builtin_expect(!!(x), 0))
+#else
+# define likely(x) (x)
+# define unlikely(x) (x)
+#endif
+
+namespace uscxml {
+
+using namespace xercesc;
+
+MicroStepFast::MicroStepFast(MicroStepCallbacks* callbacks)
+ : MicroStepImpl(callbacks), _flags(USCXML_CTX_PRISTINE), _isInitialized(false), _isCancelled(false) {
+}
+
+MicroStepFast::~MicroStepFast() {
+ for (size_t i = 0; i < _states.size(); i++) {
+ delete(_states[i]);
+ }
+ for (size_t i = 0; i < _transitions.size(); i++) {
+ delete(_transitions[i]);
+ }
+}
+
+void MicroStepFast::resortStates(DOMNode* node, const X& xmlPrefix) {
+ if (node->getNodeType() != DOMNode::ELEMENT_NODE)
+ return;
+
+ /**
+ initials
+ deep histories
+ shallow histories
+ everything else
+ */
+
+ DOMElement* element = dynamic_cast<DOMElement*>(node);
+
+ // shallow history states to top
+ DOMNode* child = element->getFirstChild();
+ while(child) {
+ resortStates(child, xmlPrefix);
+ if (child->getNodeType() == DOMNode::ELEMENT_NODE &&
+ TAGNAME_CAST(child) == xmlPrefix.str() + "history" &&
+ (!HAS_ATTR(element, "type") || iequals(ATTR(element, "type"), "shallow"))) {
+
+ DOMNode* tmp = child->getNextSibling();
+ if (child != element->getFirstChild()) {
+ element->insertBefore(child, element->getFirstChild());
+ }
+ child = tmp;
+ } else {
+ child = child->getNextSibling();
+ }
+ }
+
+ // deep history states to top
+ child = element->getFirstChild();
+ while(child) {
+ resortStates(child, xmlPrefix);
+ if (child->getNodeType() == DOMNode::ELEMENT_NODE &&
+ TAGNAME_CAST(child) == xmlPrefix.str() + "history" &&
+ HAS_ATTR(element, "type") &&
+ iequals(ATTR(element, "type"), "deep")) {
+
+ DOMNode* tmp = child->getNextSibling();
+ if (child != element->getFirstChild()) {
+ element->insertBefore(child, element->getFirstChild());
+ }
+ child = tmp;
+ } else {
+ child = child->getNextSibling();
+ }
+ }
+
+ // initial states on top of histories even
+ child = element->getFirstChild();
+ while(child) {
+ resortStates(child, xmlPrefix);
+ if (child->getNodeType() == DOMNode::ELEMENT_NODE && LOCALNAME_CAST(child) == "initial") {
+ DOMNode* tmp = child->getNextSibling();
+ if (child != element->getFirstChild()) {
+ element->insertBefore(child, element->getFirstChild());
+ }
+ child = tmp;
+ } else {
+ child = child->getNextSibling();
+ }
+ }
+}
+
+void MicroStepFast::init(xercesc::DOMElement* scxml) {
+
+ _scxml = scxml;
+ _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY);
+ _xmlPrefix = _scxml->getPrefix();
+ _xmlNS = _scxml->getNamespaceURI();
+ if (_xmlPrefix) {
+ _xmlPrefix = std::string(_xmlPrefix) + ":";
+ }
+
+ resortStates(_scxml, _xmlPrefix);
+
+ // TODO: https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/transform/ChartToC.cpp#L244
+
+
+ /** -- All things states -- */
+
+ std::set<std::string> elements;
+ elements.insert(_xmlPrefix.str() + "state");
+ elements.insert(_xmlPrefix.str() + "parallel");
+ elements.insert(_xmlPrefix.str() + "scxml");
+ elements.insert(_xmlPrefix.str() + "initial");
+ elements.insert(_xmlPrefix.str() + "final");
+ elements.insert(_xmlPrefix.str() + "history");
+
+ std::list<xercesc::DOMElement*> tmp;
+ size_t i, j;
+
+ tmp = DOMUtils::inDocumentOrder(elements, _scxml);
+ _states.resize(tmp.size());
+ _configuration.resize(tmp.size());
+ _history.resize(tmp.size());
+ _initializedData.resize(tmp.size());
+ _invocations.resize(tmp.size());
+
+ for (i = 0; i < _states.size(); i++) {
+ _states[i] = new State();
+ _states[i]->documentOrder = i;
+ _states[i]->element = tmp.front();
+ _states[i]->element->setUserData(X("uscxmlState"), _states[i], NULL);
+ _states[i]->completion.resize(_states.size());
+ _states[i]->ancestors.resize(_states.size());
+ _states[i]->children.resize(_states.size());
+ tmp.pop_front();
+ }
+ assert(tmp.size() == 0);
+
+ if (_binding == Binding::EARLY && _states.size() > 0) {
+ // add all data elements to the first state
+ std::list<DOMElement*> dataModels = DOMUtils::filterChildElements(_xmlPrefix.str() + "datamodel", _states[0]->element, true);
+ dataModels.erase(std::remove_if(dataModels.begin(),
+ dataModels.end(),
+ [](DOMElement* elem) {
+ return isInEmbeddedDocument(elem);
+ }),
+ dataModels.end());
+
+ _states[0]->data = DOMUtils::filterChildElements(_xmlPrefix.str() + "data", dataModels, false);
+ }
+
+ for (i = 0; i < _states.size(); i++) {
+ // collect states with an id attribute
+ if (HAS_ATTR(_states[i]->element, "id")) {
+ _stateIds[ATTR(_states[i]->element, "id")] = i;
+ }
+
+ // check for executable content and datamodels
+ if (_states[i]->element->getChildElementCount() > 0) {
+ _states[i]->onEntry = DOMUtils::filterChildElements(_xmlPrefix.str() + "onentry", _states[i]->element);
+ _states[i]->onExit = DOMUtils::filterChildElements(_xmlPrefix.str() + "onexit", _states[i]->element);
+ _states[i]->invoke = DOMUtils::filterChildElements(_xmlPrefix.str() + "invoke", _states[i]->element);
+
+ if (i == 0) {
+ // have global scripts as onentry of <scxml>
+ _states[i]->onEntry = DOMUtils::filterChildElements(_xmlPrefix.str() + "script", _states[i]->element, false);
+ }
+
+ std::list<DOMElement*> doneDatas = DOMUtils::filterChildElements(_xmlPrefix.str() + "donedata", _states[i]->element);
+ if (doneDatas.size() > 0) {
+ _states[i]->doneData = doneDatas.front();
+ }
+
+ if (_binding == Binding::LATE) {
+ std::list<DOMElement*> dataModels = DOMUtils::filterChildElements(_xmlPrefix.str() + "datamodel", _states[i]->element);
+ if (dataModels.size() > 0) {
+ _states[i]->data = DOMUtils::filterChildElements(_xmlPrefix.str() + "data", dataModels, false);
+ }
+ }
+ }
+
+
+ // set the states type
+ if (false) {
+ } else if (iequals(TAGNAME(_states[i]->element), _xmlPrefix.str() + "initial")) {
+ _states[i]->type = USCXML_STATE_INITIAL;
+ } else if (isFinal(_states[i]->element)) {
+ _states[i]->type = USCXML_STATE_FINAL;
+ } else if (isHistory(_states[i]->element)) {
+ if (HAS_ATTR(_states[i]->element, "type") && iequals(ATTR(_states[i]->element, "type"), "deep")) {
+ _states[i]->type = USCXML_STATE_HISTORY_DEEP;
+ } else {
+ _states[i]->type = USCXML_STATE_HISTORY_SHALLOW;
+ }
+ } else if (isAtomic(_states[i]->element)) {
+ _states[i]->type = USCXML_STATE_ATOMIC;
+ } else if (isParallel(_states[i]->element)) {
+ _states[i]->type = USCXML_STATE_PARALLEL;
+ } else if (isCompound(_states[i]->element)) {
+ _states[i]->type = USCXML_STATE_COMPOUND;
+ } else { // <scxml>
+ _states[i]->type = USCXML_STATE_COMPOUND;
+ }
+
+ // establish the states' completion
+ std::list<DOMElement*> completion = getCompletion(_states[i]->element);
+ for (j = 0; j < _states.size(); j++) {
+ if (!completion.empty() && _states[j]->element == completion.front()) {
+ _states[i]->completion[j] = true;
+ completion.pop_front();
+ } else {
+ _states[i]->completion[j] = false;
+ }
+ }
+ assert(completion.size() == 0);
+
+ // this is set when establishing the completion
+ if (_states[i]->element->getUserData(X("hasHistoryChild")) == _states[i]) {
+ _states[i]->type |= USCXML_STATE_HAS_HISTORY;
+ }
+
+ // establish the states' ancestors
+ DOMNode* parent = _states[i]->element->getParentNode();
+ if (parent && parent->getNodeType() == DOMNode::ELEMENT_NODE) {
+ State* uscxmlState = (State*)parent->getUserData(X("uscxmlState"));
+ // parent
+ _states[i]->parent = uscxmlState->documentOrder;
+ }
+
+ while(parent && parent->getNodeType() == DOMNode::ELEMENT_NODE) {
+ State* uscxmlState = (State*)parent->getUserData(X("uscxmlState"));
+
+ // ancestors
+ BIT_SET_AT(uscxmlState->documentOrder, _states[i]->ancestors);
+
+ // children
+ BIT_SET_AT(i, uscxmlState->children);
+ parent = parent->getParentNode();
+ }
+ }
+
+
+ /** -- All things transitions -- */
+
+ tmp = DOMUtils::inPostFixOrder(_xmlPrefix.str() + "transition", _scxml);
+ _transitions.resize(tmp.size());
+
+ for (i = 0; i < _transitions.size(); i++) {
+ _transitions[i] = new Transition();
+ _transitions[i]->element = tmp.front();
+ _transitions[i]->conflicts.resize(_transitions.size());
+ _transitions[i]->exitSet.resize(_states.size());
+ _transitions[i]->target.resize(_states.size());
+ tmp.pop_front();
+ }
+
+ for (i = 0; i < _transitions.size(); i++) {
+
+ // establish the transitions' exit set
+ std::list<DOMElement*> exitList = getExitSet(_transitions[i]->element, _scxml);
+ for (j = 0; j < _states.size(); j++) {
+ if (!exitList.empty() && _states[j]->element == exitList.front()) {
+ _transitions[i]->exitSet[j] = true;
+ exitList.pop_front();
+ } else {
+ _transitions[i]->exitSet[j] = false;
+ }
+ }
+ assert(exitList.size() == 0);
+
+ // establish the transitions' conflict set
+ for (j = 0; j < _transitions.size(); j++) {
+ if (conflicts(_transitions[i]->element, _transitions[j]->element, _scxml)) {
+ _transitions[i]->conflicts[j] = true;
+ } else {
+ _transitions[i]->conflicts[j] = false;
+ }
+ }
+
+ // establish the transitions' target set
+ std::list<std::string> targets = tokenize(ATTR(_transitions[i]->element, "target"));
+ for (auto tIter = targets.begin(); tIter != targets.end(); tIter++) {
+ if (_stateIds.find(*tIter) != _stateIds.end()) {
+ _transitions[i]->target[_stateIds[*tIter]] = true;
+ }
+ }
+
+ // the transition's source
+ State* uscxmlState = (State*)(_transitions[i]->element->getParentNode()->getUserData(X("uscxmlState")));
+ _transitions[i]->source = uscxmlState->documentOrder;
+
+
+ // the transition's type
+ if (!HAS_ATTR(_transitions[i]->element, "target")) {
+ _transitions[i]->type |= USCXML_TRANS_TARGETLESS;
+ }
+
+ if (HAS_ATTR(_transitions[i]->element, "type") && iequals(ATTR(_transitions[i]->element, "type"), "internal")) {
+ _transitions[i]->type |= USCXML_TRANS_INTERNAL;
+ }
+
+ if (!HAS_ATTR(_transitions[i]->element, "event")) {
+ _transitions[i]->type |= USCXML_TRANS_SPONTANEOUS;
+ }
+
+ if (iequals(TAGNAME_CAST(_transitions[i]->element->getParentNode()), _xmlPrefix.str() + "history")) {
+ _transitions[i]->type |= USCXML_TRANS_HISTORY;
+ }
+
+ if (iequals(TAGNAME_CAST(_transitions[i]->element->getParentNode()), _xmlPrefix.str() + "initial")) {
+ _transitions[i]->type |= USCXML_TRANS_INITIAL;
+ }
+
+
+ // the transitions event and condition
+ _transitions[i]->event = (HAS_ATTR(_transitions[i]->element, "event") ? ATTR(_transitions[i]->element, "event") : "");
+ _transitions[i]->cond = (HAS_ATTR(_transitions[i]->element, "cond") ? ATTR(_transitions[i]->element, "cond") : "");
+
+ // is there executable content?
+ if (_transitions[i]->element->getChildElementCount() > 0) {
+ _transitions[i]->onTrans = _transitions[i]->element;
+ }
+
+ }
+ _isInitialized = true;
+}
+
+void MicroStepFast::markAsCancelled() {
+ _isCancelled = true;
+}
+
+InterpreterState MicroStepFast::step(bool blocking) {
+ if (!_isInitialized) {
+ init(_scxml);
+ return USCXML_INITIALIZED;
+ }
+
+ size_t i, j, k;
+
+ boost::dynamic_bitset<> exitSet(_states.size(), false);
+ boost::dynamic_bitset<> entrySet(_states.size(), false);
+ boost::dynamic_bitset<> targetSet(_states.size(), false);
+ boost::dynamic_bitset<> tmpStates(_states.size(), false);
+
+ boost::dynamic_bitset<> conflicts(_transitions.size(), false);
+ boost::dynamic_bitset<> transSet(_transitions.size(), false);
+
+#ifdef USCXML_VERBOSE
+ std::cerr << "Config: ";
+ printStateNames(_configuration);
+#endif
+
+ if (_flags & USCXML_CTX_FINISHED)
+ return USCXML_FINISHED;
+
+ if (_flags & USCXML_CTX_TOP_LEVEL_FINAL) {
+ USCXML_MONITOR_CALLBACK(_callbacks->getMonitor(), beforeCompletion);
+
+ /* exit all remaining states */
+ i = USCXML_NUMBER_STATES;
+ while(i-- > 0) {
+ if (BIT_HAS(i, _configuration)) {
+ /* call all on exit handlers */
+ for (auto exitIter = USCXML_GET_STATE(i).onExit.begin(); exitIter != USCXML_GET_STATE(i).onExit.end(); exitIter++) {
+ try {
+ _callbacks->process(*exitIter);
+ } catch (...) {
+ // do nothing and continue with next block
+ }
+ }
+ /* Leave configuration intact */
+// BIT_CLEAR(i, _configuration);
+ }
+
+ if (BIT_HAS(i, _invocations)) {
+ /* cancel all invokers */
+ if (USCXML_GET_STATE(i).invoke.size() > 0) {
+ for (auto invIter = USCXML_GET_STATE(i).invoke.begin(); invIter != USCXML_GET_STATE(i).invoke.end(); invIter++) {
+ _callbacks->uninvoke(*invIter);
+ }
+ }
+ BIT_CLEAR(i, _invocations);
+ }
+ }
+
+ _flags |= USCXML_CTX_FINISHED;
+
+ USCXML_MONITOR_CALLBACK(_callbacks->getMonitor(), afterCompletion);
+
+ return USCXML_FINISHED;
+ }
+
+
+ if (_flags == USCXML_CTX_PRISTINE) {
+
+ targetSet |= USCXML_GET_STATE(0).completion;
+ _flags |= USCXML_CTX_SPONTANEOUS | USCXML_CTX_INITIALIZED;
+ USCXML_MONITOR_CALLBACK(_callbacks->getMonitor(), beforeMicroStep);
+
+ goto ESTABLISH_ENTRYSET;
+ }
+
+ if (_flags & USCXML_CTX_SPONTANEOUS) {
+ _event = Event();
+ goto SELECT_TRANSITIONS;
+ }
+
+
+ if ((_event = _callbacks->dequeueInternal())) {
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeProcessingEvent, _event);
+ goto SELECT_TRANSITIONS;
+ }
+
+ /* manage invocations */
+ for (i = 0; i < USCXML_NUMBER_STATES; i++) {
+ /* uninvoke */
+ if (!BIT_HAS(i, _configuration) && BIT_HAS(i, _invocations)) {
+ if (USCXML_GET_STATE(i).invoke.size() > 0) {
+ for (auto invIter = USCXML_GET_STATE(i).invoke.begin(); invIter != USCXML_GET_STATE(i).invoke.end(); invIter++) {
+ _callbacks->uninvoke(*invIter);
+ }
+ }
+ BIT_CLEAR(i, _invocations)
+ }
+ /* invoke */
+ if (BIT_HAS(i, _configuration) && !BIT_HAS(i, _invocations)) {
+ if (USCXML_GET_STATE(i).invoke.size() > 0) {
+ for (auto invIter = USCXML_GET_STATE(i).invoke.begin(); invIter != USCXML_GET_STATE(i).invoke.end(); invIter++) {
+ try {
+ _callbacks->invoke(*invIter);
+ } catch (...) {
+ }
+ }
+ }
+ BIT_SET_AT(i, _invocations)
+ }
+ }
+
+ // we dequeued all internal events and ought to signal stable configuration
+ if (!(_flags & USCXML_CTX_STABLE)) {
+ USCXML_MONITOR_CALLBACK(_callbacks->getMonitor(), onStableConfiguration);
+ _microstepConfigurations.clear();
+ _flags |= USCXML_CTX_STABLE;
+ }
+
+ if ((_event = _callbacks->dequeueExternal(blocking))) {
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeProcessingEvent, _event);
+ goto SELECT_TRANSITIONS;
+ }
+
+ if (_isCancelled) {
+ // finalize and exit
+ _flags |= USCXML_CTX_TOP_LEVEL_FINAL;
+ return USCXML_CANCELLED;
+ }
+
+// if (blocking) // we received the empty event to unblock
+// return USCXML_IDLE; // we return IDLE nevertheless
+
+ return USCXML_IDLE;
+
+SELECT_TRANSITIONS:
+
+ // we read an event - unset stable to signal onstable again later
+ _flags &= ~USCXML_CTX_STABLE;
+
+ for (i = 0; i < _transitions.size(); i++) {
+ /* never select history or initial transitions automatically */
+ if unlikely(USCXML_GET_TRANS(i).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL))
+ continue;
+
+ /* is the transition active? */
+ if (BIT_HAS(USCXML_GET_TRANS(i).source, _configuration)) {
+ /* is it non-conflicting? */
+ if (!BIT_HAS(i, conflicts)) {
+ /* is it spontaneous with an event or vice versa? */
+ if ((USCXML_GET_TRANS(i).event.size() == 0 && !_event) ||
+ (USCXML_GET_TRANS(i).event.size() != 0 && _event)) {
+ /* is it enabled? */
+ if ((!_event || _callbacks->isMatched(_event, USCXML_GET_TRANS(i).event)) &&
+ (USCXML_GET_TRANS(i).cond.size() == 0 || _callbacks->isTrue(USCXML_GET_TRANS(i).cond))) {
+
+ /* remember that we found a transition */
+ _flags |= USCXML_CTX_TRANSITION_FOUND;
+
+ /* transitions that are pre-empted */
+ conflicts |= USCXML_GET_TRANS(i).conflicts;
+
+ /* states that are directly targeted (resolve as entry-set later) */
+ targetSet |= USCXML_GET_TRANS(i).target;
+
+ /* states that will be left */
+ exitSet |= USCXML_GET_TRANS(i).exitSet;
+
+ BIT_SET_AT(i, transSet);
+ }
+ }
+ }
+ }
+ }
+
+#ifdef USCXML_VERBOSE
+ std::cerr << "Complete Exit: ";
+ printStateNames(exitSet);
+#endif
+
+ exitSet &= _configuration;
+
+ if (_flags & USCXML_CTX_TRANSITION_FOUND) {
+ // trigger more sppontaneuous transitions
+ _flags |= USCXML_CTX_SPONTANEOUS;
+ _flags &= ~USCXML_CTX_TRANSITION_FOUND;
+ } else {
+ // spontaneuous transitions are exhausted
+ _flags &= ~USCXML_CTX_SPONTANEOUS;
+ return USCXML_MACROSTEPPED;
+ }
+
+ USCXML_MONITOR_CALLBACK(_callbacks->getMonitor(), beforeMicroStep);
+
+#ifdef USCXML_VERBOSE
+ std::cerr << "Targets: ";
+ printStateNames(targetSet);
+#endif
+
+#ifdef USCXML_VERBOSE
+ std::cerr << "Exiting: ";
+ printStateNames(exitSet);
+#endif
+
+#ifdef USCXML_VERBOSE
+ std::cerr << "History: ";
+ printStateNames(_history);
+#endif
+
+
+ /* REMEMBER_HISTORY: */
+ for (i = 0; i < _states.size(); i++) {
+ if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_SHALLOW ||
+ USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP) {
+ /* a history state whose parent is about to be exited */
+ if unlikely(BIT_HAS(USCXML_GET_STATE(i).parent, exitSet)) {
+ tmpStates = USCXML_GET_STATE(i).completion;
+
+ /* set those states who were enabled */
+ tmpStates &= _configuration;
+
+ /* clear current history with completion mask */
+ _history &= ~(USCXML_GET_STATE(i).completion);
+
+ /* set history */
+ _history |= tmpStates;
+
+ }
+ }
+ }
+
+ESTABLISH_ENTRYSET:
+ /* calculate new entry set */
+ entrySet = targetSet;
+
+ /* iterate for ancestors */
+ i = entrySet.find_first();
+ while(i != boost::dynamic_bitset<>::npos) {
+ entrySet |= USCXML_GET_STATE(i).ancestors;
+ i = entrySet.find_next(i);
+ }
+
+ /* iterate for descendants */
+ i = entrySet.find_first();
+ while(i != boost::dynamic_bitset<>::npos) {
+
+
+ switch (USCXML_STATE_MASK(USCXML_GET_STATE(i).type)) {
+ case USCXML_STATE_FINAL:
+ case USCXML_STATE_ATOMIC:
+ break;
+
+ case USCXML_STATE_PARALLEL: {
+ entrySet |= USCXML_GET_STATE(i).completion;
+ break;
+ }
+
+ case USCXML_STATE_HISTORY_SHALLOW:
+ case USCXML_STATE_HISTORY_DEEP: {
+ if (!BIT_HAS_AND(USCXML_GET_STATE(i).completion, _history) &&
+ !BIT_HAS(USCXML_GET_STATE(i).parent, _configuration)) {
+
+ /* nothing set for history, look for a default transition */
+ for (j = 0; j < _transitions.size(); j++) {
+ if unlikely(USCXML_GET_TRANS(j).source == i) {
+ entrySet |= USCXML_GET_TRANS(j).target;
+
+ if(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP &&
+ !BIT_HAS_AND(USCXML_GET_TRANS(j).target, USCXML_GET_STATE(i).children)) {
+ for (k = i + 1; k < _states.size(); k++) {
+ if (BIT_HAS(k, USCXML_GET_TRANS(j).target)) {
+ entrySet |= USCXML_GET_STATE(k).ancestors;
+ break;
+ }
+ }
+ }
+ BIT_SET_AT(j, transSet);
+ break;
+ }
+ /* Note: SCXML mandates every history to have a transition! */
+ }
+ } else {
+ tmpStates = USCXML_GET_STATE(i).completion;
+ tmpStates &= _history;
+ entrySet |= tmpStates;
+
+ if (USCXML_GET_STATE(i).type == (USCXML_STATE_HAS_HISTORY | USCXML_STATE_HISTORY_DEEP)) {
+ /* a deep history state with nested histories -> more completion */
+ for (j = i + 1; j < USCXML_NUMBER_STATES; j++) {
+ if (BIT_HAS(j, USCXML_GET_STATE(i).completion) &&
+ BIT_HAS(j, entrySet) &&
+ (USCXML_GET_STATE(j).type & USCXML_STATE_HAS_HISTORY)) {
+ for (k = j + 1; k < USCXML_NUMBER_STATES; k++) {
+ /* add nested history to entry_set */
+ if ((USCXML_STATE_MASK(USCXML_GET_STATE(k).type) == USCXML_STATE_HISTORY_DEEP ||
+ USCXML_STATE_MASK(USCXML_GET_STATE(k).type) == USCXML_STATE_HISTORY_SHALLOW) &&
+ BIT_HAS(k, USCXML_GET_STATE(j).children)) {
+ /* a nested history state */
+ BIT_SET_AT(k, entrySet);
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case USCXML_STATE_INITIAL: {
+ for (j = 0; j < USCXML_NUMBER_TRANS; j++) {
+ if (USCXML_GET_TRANS(j).source == i) {
+ BIT_SET_AT(j, transSet);
+ BIT_CLEAR(i, entrySet);
+ entrySet |= USCXML_GET_TRANS(j).target;
+ for (k = i + 1; k < USCXML_NUMBER_STATES; k++) {
+ if (BIT_HAS(k, USCXML_GET_TRANS(j).target)) {
+ entrySet |= USCXML_GET_STATE(k).ancestors;
+ }
+ }
+ }
+ }
+ break;
+ }
+ case USCXML_STATE_COMPOUND: { /* we need to check whether one child is already in entry_set */
+ if (!BIT_HAS_AND(entrySet, USCXML_GET_STATE(i).children) &&
+ (!BIT_HAS_AND(_configuration, USCXML_GET_STATE(i).children) ||
+ BIT_HAS_AND(exitSet, USCXML_GET_STATE(i).children))) {
+ entrySet |= USCXML_GET_STATE(i).completion;
+ if (!BIT_HAS_AND(USCXML_GET_STATE(i).completion, USCXML_GET_STATE(i).children)) {
+ /* deep completion */
+ for (j = i + 1; j < USCXML_NUMBER_STATES; j++) {
+ if (BIT_HAS(j, USCXML_GET_STATE(i).completion)) {
+ entrySet |= USCXML_GET_STATE(j).ancestors;
+ break; /* completion of compound is single state */
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ i = entrySet.find_next(i);
+
+ }
+
+
+#ifdef USCXML_VERBOSE
+ std::cerr << "Transitions: " << transSet << std::endl;
+#endif
+
+ /* EXIT_STATES: */
+ /* we cannot use find_first due to ordering */
+ i = USCXML_NUMBER_STATES;
+ while(i-- > 0) {
+ if (BIT_HAS(i, exitSet) && BIT_HAS(i, _configuration)) {
+
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeExitingState, USCXML_GET_STATE(i).element);
+
+ /* call all on exit handlers */
+ for (auto exitIter = USCXML_GET_STATE(i).onExit.begin(); exitIter != USCXML_GET_STATE(i).onExit.end(); exitIter++) {
+ try {
+ _callbacks->process(*exitIter);
+ } catch (...) {
+ // do nothing and continue with next block
+ }
+ }
+ BIT_CLEAR(i, _configuration);
+
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), afterExitingState, USCXML_GET_STATE(i).element);
+
+ }
+ }
+
+ /* TAKE_TRANSITIONS: */
+ i = transSet.find_first();
+ while(i != boost::dynamic_bitset<>::npos) {
+ if ((USCXML_GET_TRANS(i).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) == 0) {
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeTakingTransition, USCXML_GET_TRANS(i).element);
+
+ if (USCXML_GET_TRANS(i).onTrans != NULL) {
+
+ /* call executable content in non-history, non-initial transition */
+ try {
+ _callbacks->process(USCXML_GET_TRANS(i).onTrans);
+ } catch (...) {
+ // do nothing and continue with next block
+ }
+ }
+
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), afterTakingTransition, USCXML_GET_TRANS(i).element);
+
+ }
+ i = transSet.find_next(i);
+ }
+
+#ifdef USCXML_VERBOSE
+ std::cerr << "Entering: ";
+ printStateNames(entrySet);
+#endif
+
+
+ /* ENTER_STATES: */
+ i = entrySet.find_first();
+ while(i != boost::dynamic_bitset<>::npos) {
+
+ if (BIT_HAS(i, _configuration)) {
+ // already active
+ i = entrySet.find_next(i);
+ continue;
+ }
+
+ /* these are no proper states */
+ if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP ||
+ USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_SHALLOW ||
+ USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_INITIAL) {
+ i = entrySet.find_next(i);
+ continue;
+ }
+
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeEnteringState, USCXML_GET_STATE(i).element);
+
+ BIT_SET_AT(i, _configuration);
+
+ /* initialize data */
+ if (!BIT_HAS(i, _initializedData)) {
+ for (auto dataIter = USCXML_GET_STATE(i).data.begin(); dataIter != USCXML_GET_STATE(i).data.end(); dataIter++) {
+ _callbacks->initData(*dataIter);
+ }
+ BIT_SET_AT(i, _initializedData);
+ }
+
+ /* call all on entry handlers */
+ for (auto entryIter = USCXML_GET_STATE(i).onEntry.begin(); entryIter != USCXML_GET_STATE(i).onEntry.end(); entryIter++) {
+ try {
+ _callbacks->process(*entryIter);
+ } catch (...) {
+ // do nothing and continue with next block
+ }
+ }
+
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), afterEnteringState, USCXML_GET_STATE(i).element);
+
+ /* take history and initial transitions */
+ for (j = 0; j < USCXML_NUMBER_TRANS; j++) {
+ if unlikely(BIT_HAS(j, transSet) &&
+ (USCXML_GET_TRANS(j).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) &&
+ USCXML_GET_STATE(USCXML_GET_TRANS(j).source).parent == i) {
+
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeTakingTransition, USCXML_GET_TRANS(j).element);
+
+ /* call executable content in transition */
+ if (USCXML_GET_TRANS(j).onTrans != NULL) {
+ try {
+ _callbacks->process(USCXML_GET_TRANS(j).onTrans);
+ } catch (...) {
+ // do nothing and continue with next block
+ }
+ }
+
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), afterTakingTransition, USCXML_GET_TRANS(j).element);
+
+ }
+ }
+
+ /* handle final states */
+ if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_FINAL) {
+ if unlikely(USCXML_GET_STATE(i).ancestors.count() == 1 && BIT_HAS(0, USCXML_GET_STATE(i).ancestors)) {
+ // only the topmost scxml is an ancestor
+ _flags |= USCXML_CTX_TOP_LEVEL_FINAL;
+ } else {
+ /* raise done event */
+ _callbacks->raiseDoneEvent(USCXML_GET_STATE(USCXML_GET_STATE(i).parent).element, USCXML_GET_STATE(i).doneData);
+ }
+
+ /**
+ * are we the last final state to leave a parallel state?:
+ * 1. Gather all parallel states in our ancestor chain
+ * 2. Find all states for which these parallels are ancestors
+ * 3. Iterate all active final states and remove their ancestors
+ * 4. If a state remains, not all children of a parallel are final
+ */
+ for (j = 0; j < USCXML_NUMBER_STATES; j++) {
+ if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(j).type) == USCXML_STATE_PARALLEL &&
+ BIT_HAS(j, USCXML_GET_STATE(i).ancestors)) {
+ tmpStates.reset();
+ k = _configuration.find_first();
+ while (k != boost::dynamic_bitset<>::npos) {
+ if (BIT_HAS(j, USCXML_GET_STATE(k).ancestors)) {
+ if (USCXML_STATE_MASK(USCXML_GET_STATE(k).type) == USCXML_STATE_FINAL) {
+ tmpStates ^= USCXML_GET_STATE(k).ancestors;
+ } else {
+ BIT_SET_AT(k, tmpStates);
+ }
+ }
+ k = _configuration.find_next(k);
+ }
+ if (!tmpStates.any()) {
+ // raise done for state j
+ _callbacks->raiseDoneEvent(USCXML_GET_STATE(j).element, USCXML_GET_STATE(j).doneData);
+ }
+ }
+ }
+ }
+ }
+ USCXML_MONITOR_CALLBACK(_callbacks->getMonitor(), afterMicroStep);
+
+ // are we running in circles?
+ if (_microstepConfigurations.find(_configuration) != _microstepConfigurations.end()) {
+ USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(),
+ reportIssue,
+ InterpreterIssue("Reentering same configuration during microstep - possible endless loop",
+ NULL,
+ InterpreterIssue::USCXML_ISSUE_WARNING));
+ }
+ _microstepConfigurations.insert(_configuration);
+
+ return USCXML_MICROSTEPPED;
+}
+
+void MicroStepFast::reset() {
+ _isCancelled = false;
+ _flags = USCXML_CTX_PRISTINE;
+ _configuration.reset();
+ _history.reset();
+ _initializedData.reset();
+ _invocations.reset();
+
+}
+
+bool MicroStepFast::isInState(const std::string& stateId) {
+#ifdef USCXML_VERBOSE
+ printStateNames(_configuration);
+#endif
+ if (_stateIds.find(stateId) == _stateIds.end())
+ return false;
+ return _configuration[_stateIds[stateId]];
+}
+
+std::list<xercesc::DOMElement*> MicroStepFast::getConfiguration() {
+ std::list<xercesc::DOMElement*> config;
+ size_t i = _configuration.find_first();
+ while(i != boost::dynamic_bitset<>::npos) {
+ config.push_back(_states[i]->element);
+ i = _configuration.find_next(i);
+ }
+ return config;
+}
+
+
+std::list<DOMElement*> MicroStepFast::getHistoryCompletion(const DOMElement* history) {
+ std::set<std::string> elements;
+ elements.insert(_xmlPrefix.str() + "history");
+ std::list<DOMElement*> histories = DOMUtils::inPostFixOrder(elements, _scxml);
+
+ std::list<DOMElement*> covered;
+ std::list<DOMElement*> perParentcovered;
+ const DOMNode* parent = NULL;
+
+ std::list<DOMElement*> completion;
+
+
+ if (parent != history->getParentNode()) {
+ covered.insert(covered.end(), perParentcovered.begin(), perParentcovered.end());
+ perParentcovered.clear();
+ parent = history->getParentNode();
+ }
+
+ bool deep = (HAS_ATTR(history, "type") && iequals(ATTR(history, "type"), "deep"));
+
+ for (size_t j = 0; j < _states.size(); j++) {
+ if (_states[j]->element == history)
+ continue;
+
+ if (DOMUtils::isDescendant((DOMNode*)_states[j]->element, history->getParentNode()) && isHistory(_states[j]->element)) {
+ ((DOMElement*)history)->setUserData(X("hasHistoryChild"), _states[j], NULL);
+ }
+
+ if (DOMUtils::isMember(_states[j]->element, covered))
+ continue;
+
+ if (deep) {
+ if (DOMUtils::isDescendant(_states[j]->element, history->getParentNode()) && !isHistory(_states[j]->element)) {
+ completion.push_back(_states[j]->element);
+ }
+ } else {
+ if (_states[j]->element->getParentNode() == history->getParentNode() && !isHistory(_states[j]->element)) {
+ completion.push_back(_states[j]->element);
+ }
+ }
+ }
+
+ return completion;
+}
+
+#ifdef USCXML_VERBOSE
+/**
+ * Print name of states contained in a (debugging).
+ */
+void MicroStepFast::printStateNames(const boost::dynamic_bitset<>& a) {
+ size_t i;
+ const char* seperator = "";
+ for (i = 0; i < a.size(); i++) {
+ if (BIT_HAS(i, a)) {
+ std::cerr << seperator << (HAS_ATTR(USCXML_GET_STATE(i).element, X("id")) ? ATTR(USCXML_GET_STATE(i).element, X("id")) : "UNK");
+ seperator = ", ";
+ }
+ }
+ std::cerr << std::endl;
+}
+#endif
+
+std::list<DOMElement*> MicroStepFast::getCompletion(const DOMElement* state) {
+
+ if (isHistory(state)) {
+ // we already did in setHistoryCompletion
+ return getHistoryCompletion(state);
+
+ } else if (isParallel(state)) {
+ return getChildStates(state);
+
+ } else if (HAS_ATTR(state, "initial")) {
+ return getStates(tokenize(ATTR(state, "initial")), _scxml);
+
+ } else {
+ std::list<DOMElement*> completion;
+
+ std::list<DOMElement*> initElems = DOMUtils::filterChildElements(_xmlPrefix.str() + "initial", state);
+ if(initElems.size() > 0) {
+ // initial element is first child
+ completion.push_back(initElems.front());
+ } else {
+ // first child state
+ DOMNodeList* children = state->getChildNodes();
+ for (size_t i = 0; i < children->getLength(); i++) {
+ if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE)
+ continue;
+ if (isState(dynamic_cast<DOMElement*>(children->item(i)))) {
+ completion.push_back(dynamic_cast<DOMElement*>(children->item(i)));
+ break;
+ }
+ }
+ }
+ return completion;
+ }
+
+}
+
+
+#if 0
+/**
+ * See: http://www.w3.org/TR/scxml/#LegalStateConfigurations
+ */
+bool MicroStepFast::hasLegalConfiguration() {
+
+ // The configuration contains exactly one child of the <scxml> element.
+ std::list<DOMElement*> scxmlChilds = getChildStates(_scxml, true);
+ DOMNode* foundScxmlChild;
+ for (auto sIter = scxmlChilds.begin(); sIter != scxmlChilds.end(); sIter++) {
+ DOMElement* state = *sIter;
+ if (isMember(state, config)) {
+ if (foundScxmlChild) {
+ LOG(ERROR) << "Invalid configuration: Multiple childs of scxml root are active '" << ATTR_CAST(foundScxmlChild, "id") << "' and '" << ATTR_CAST(scxmlChilds[i], "id") << "'";
+ return false;
+ }
+ foundScxmlChild = scxmlChilds[i];
+ }
+ }
+ if (!foundScxmlChild) {
+ LOG(ERROR) << "Invalid configuration: No childs of scxml root are active";
+
+ return false;
+ }
+
+ // The configuration contains one or more atomic states.
+ bool foundAtomicState = false;
+ for (size_t i = 0; i < config.size(); i++) {
+ if (isAtomic(Element<std::string>(config[i]))) {
+ foundAtomicState = true;
+ break;
+ }
+ }
+ if (!foundAtomicState) {
+ LOG(ERROR) << "Invalid configuration: No atomic state is active";
+ return false;
+ }
+
+ // the configuration contains no history pseudo-states
+ for (size_t i = 0; i < config.size(); i++) {
+ if (isHistory(Element<std::string>(config[i]))) {
+ LOG(ERROR) << "Invalid configuration: history state " << ATTR_CAST(config[i], "id") << " is active";
+ return false;
+ }
+ }
+
+
+
+ // When the configuration contains an atomic state, it contains all of its <state> and <parallel> ancestors.
+ for (size_t i = 0; i < config.size(); i++) {
+ if (isAtomic(Element<std::string>(config[i]))) {
+ Node<std::string> parent = config[i];
+ while(((parent = parent.getParentNode()) && parent.getNodeType() == Node_base::ELEMENT_NODE)) {
+ if (isState(Element<std::string>(parent)) &&
+ (iequals(LOCALNAME(parent), "state") ||
+ iequals(LOCALNAME(parent), "parallel"))) {
+ if (!isMember(parent, config)) {
+ LOG(ERROR) << "Invalid configuration: atomic state '" << ATTR_CAST(config[i], "id") << "' is active, but parent '" << ATTR_CAST(parent, "id") << "' is not";
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ // When the configuration contains a non-atomic <state>, it contains one and only one of the state's children
+ for (size_t i = 0; i < config.size(); i++) {
+ Element<std::string> configElem(config[i]);
+ if (!isAtomic(configElem) && !isParallel(configElem)) {
+ Node<std::string> foundChildState;
+ //std::cerr << config[i] << std::endl;
+ NodeSet<std::string> childs = getChildStates(config[i]);
+ for (size_t j = 0; j < childs.size(); j++) {
+ //std::cerr << childs[j] << std::endl;
+ if (isMember(childs[j], config)) {
+ if (foundChildState) {
+ LOG(ERROR) << "Invalid configuration: Multiple childs of compound '" << ATTR_CAST(config[i], "id")
+ << "' are active '" << ATTR_CAST(foundChildState, "id") << "' and '" << ATTR_CAST(childs[j], "id") << "'";
+ return false;
+ }
+ foundChildState = childs[j];
+ }
+ }
+ if (!foundChildState) {
+ LOG(ERROR) << "Invalid configuration: No childs of compound '" << ATTR_CAST(config[i], "id") << "' are active";
+ return false;
+ }
+ }
+ }
+
+ // If the configuration contains a <parallel> state, it contains all of its children
+ for (size_t i = 0; i < config.size(); i++) {
+ if (isParallel(Element<std::string>(config[i]))) {
+ NodeSet<std::string> childs = getChildStates(config[i]);
+ for (size_t j = 0; j < childs.size(); j++) {
+ if (!isMember(childs[j], config) && !isHistory(Element<std::string>(childs[j]))) {
+ LOG(ERROR) << "Invalid configuration: Not all children of parallel '" << ATTR_CAST(config[i], "id") << "' are active i.e. '" << ATTR_CAST(childs[j], "id") << "' is not";
+ return false;
+ }
+ }
+ }
+ }
+
+ // everything worked out fine!
+ return true;
+}
+#endif
+
+} \ No newline at end of file
diff --git a/src/uscxml/interpreter/MicroStepFast.h b/src/uscxml/interpreter/MicroStepFast.h
new file mode 100644
index 0000000..c41be80
--- /dev/null
+++ b/src/uscxml/interpreter/MicroStepFast.h
@@ -0,0 +1,127 @@
+/**
+ * @file
+ * @author 2012-2016 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#ifndef INTERPRETERFAST_H_DA55E52B
+#define INTERPRETERFAST_H_DA55E52B
+
+//#define USCXML_VERBOSE 1
+
+#include <vector>
+#include <set>
+#include "MicroStepImpl.h"
+
+#include <boost/dynamic_bitset.hpp>
+
+namespace uscxml {
+
+class MicroStepFast : public MicroStepImpl {
+public:
+ MicroStepFast(MicroStepCallbacks* callbacks);
+ virtual ~MicroStepFast();
+
+ virtual InterpreterState step(bool blocking);
+ virtual void reset();
+ virtual bool isInState(const std::string& stateId);
+ virtual std::list<xercesc::DOMElement*> getConfiguration();
+ void markAsCancelled();
+
+protected:
+ class Transition {
+ public:
+ Transition() : element(NULL), source(0), onTrans(NULL), type(0) {}
+
+ xercesc::DOMElement* element;
+ boost::dynamic_bitset<> conflicts;
+ boost::dynamic_bitset<> exitSet;
+
+ uint32_t source;
+ boost::dynamic_bitset<> target;
+
+ xercesc::DOMElement* onTrans;
+
+ std::string event;
+ std::string cond;
+
+ unsigned char type;
+
+ };
+
+ class State {
+ public:
+ State() : element(NULL), parent(0), documentOrder(0), doneData(NULL), type(0) {}
+
+ xercesc::DOMElement* element;
+ boost::dynamic_bitset<> completion;
+ boost::dynamic_bitset<> children;
+ boost::dynamic_bitset<> ancestors;
+ uint32_t parent;
+ uint32_t documentOrder;
+
+ std::list<xercesc::DOMElement*> data;
+ std::list<xercesc::DOMElement*> invoke;
+ std::list<xercesc::DOMElement*> onEntry;
+ std::list<xercesc::DOMElement*> onExit;
+ xercesc::DOMElement* doneData;
+
+ unsigned char type;
+ };
+
+ virtual void init(xercesc::DOMElement* scxml);
+
+ std::list<xercesc::DOMElement*> getCompletion(const xercesc::DOMElement* state);
+
+ unsigned char _flags;
+ std::map<std::string, int> _stateIds;
+
+ std::vector<State*> _states;
+ std::vector<Transition*> _transitions;
+ std::list<xercesc::DOMElement*> _globalScripts;
+
+ boost::dynamic_bitset<> _configuration;
+ boost::dynamic_bitset<> _invocations;
+ boost::dynamic_bitset<> _history;
+ boost::dynamic_bitset<> _initializedData;
+
+ std::set<boost::dynamic_bitset<> > _microstepConfigurations;
+
+ Binding _binding;
+ xercesc::DOMElement* _scxml;
+ X _xmlPrefix;
+ X _xmlNS;
+
+ bool _isInitialized;
+ bool _isCancelled;
+ Event _event; // we do not care about the event's representation
+
+private:
+ std::list<xercesc::DOMElement*> getHistoryCompletion(const xercesc::DOMElement* state);
+ void resortStates(xercesc::DOMNode* node, const X& xmlPrefix);
+
+// bool hasLegalConfiguration();
+
+#ifdef USCXML_VERBOSE
+ void printStateNames(const boost::dynamic_bitset<>& bitset);
+#endif
+
+};
+
+}
+
+#endif /* end of include guard: INTERPRETERFAST_H_DA55E52B */
+
diff --git a/src/uscxml/interpreter/MicroStepImpl.h b/src/uscxml/interpreter/MicroStepImpl.h
new file mode 100644
index 0000000..71c03b5
--- /dev/null
+++ b/src/uscxml/interpreter/MicroStepImpl.h
@@ -0,0 +1,127 @@
+/**
+ * @file
+ * @author 2016 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#ifndef MICROSTEPIMPL_H_98233709
+#define MICROSTEPIMPL_H_98233709
+
+#include <memory>
+#include <mutex>
+#include <list>
+#include <map>
+#include <string>
+
+#include "uscxml/Common.h"
+#include "uscxml/messages/Event.h"
+#include "uscxml/interpreter/InterpreterMonitor.h"
+#include "uscxml/util/DOM.h"
+#include <xercesc/dom/DOM.hpp>
+
+namespace uscxml {
+
+enum InterpreterState {
+
+
+ USCXML_FINISHED = -2, ///< machine reached a final configuration and is done
+ USCXML_INTERRUPTED = -1, ///< machine received the empty event on the external queue
+ USCXML_UNDEF = 0, ///< not an actual state
+ USCXML_IDLE = 1, ///< stable configuration and queues empty
+ USCXML_INITIALIZED = 2, ///< DOM is setup and all external components instantiated
+ USCXML_INSTANTIATED = 3, ///< nothing really, just instantiated
+ USCXML_MICROSTEPPED = 4, ///< processed one transition set
+ USCXML_MACROSTEPPED = 5, ///< processed all transition sets and reached a stable configuration
+ USCXML_CANCELLED = 6, ///< machine was cancelled, step once more to finalize
+};
+
+class USCXML_API MicroStepCallbacks {
+public:
+ /** Event Queues / Matching */
+ virtual Event dequeueInternal() = 0;
+ virtual Event dequeueExternal(bool blocking) = 0;
+ virtual bool isMatched(const Event& event, const std::string& eventDesc) = 0;
+ virtual void raiseDoneEvent(xercesc::DOMElement* state, xercesc::DOMElement* doneData) = 0;
+
+ /** Datamodel */
+ virtual bool isTrue(const std::string& expr) = 0;
+ virtual void initData(xercesc::DOMElement* element) = 0;
+
+ /** Executable Content */
+ virtual void process(xercesc::DOMElement* block) = 0;
+
+ /** Invocations */
+ virtual void invoke(xercesc::DOMElement* invoke) = 0;
+ virtual void uninvoke(xercesc::DOMElement* invoke) = 0;
+
+ /** Monitoring */
+ virtual InterpreterMonitor* getMonitor() = 0;
+};
+
+class USCXML_API MicroStepImpl {
+public:
+ enum Binding {
+ EARLY = 0,
+ LATE = 1
+ };
+
+ MicroStepImpl(MicroStepCallbacks* callbacks) : _callbacks(callbacks) {}
+
+ virtual InterpreterState step(bool blocking) = 0;
+ virtual void reset() = 0; ///< Reset state machine
+ virtual bool isInState(const std::string& stateId) = 0;
+ virtual std::list<xercesc::DOMElement*> getConfiguration() = 0;
+
+ virtual void init(xercesc::DOMElement* scxml) = 0;
+ virtual void markAsCancelled() = 0;
+
+protected:
+ MicroStepCallbacks* _callbacks;
+
+};
+
+class USCXML_API MicroStep {
+public:
+ PIMPL_OPERATORS(MicroStep)
+
+ virtual InterpreterState step(bool blocking) {
+ return _impl->step(blocking);
+ }
+ virtual void reset() {
+ return _impl->reset();
+ }
+ virtual bool isInState(const std::string& stateId) {
+ return _impl->isInState(stateId);
+ }
+
+ std::list<xercesc::DOMElement*> getConfiguration() {
+ return _impl->getConfiguration();
+ }
+
+ virtual void init(xercesc::DOMElement* scxml) {
+ _impl->init(scxml);
+ }
+
+ virtual void markAsCancelled() {
+ _impl->markAsCancelled();
+ }
+protected:
+ std::shared_ptr<MicroStepImpl> _impl;
+};
+
+}
+
+#endif /* end of include guard: MICROSTEPIMPL_H_98233709 */