summaryrefslogtreecommitdiffstats
path: root/src/uscxml
diff options
context:
space:
mode:
authorStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2013-03-28 23:28:46 (GMT)
committerStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2013-03-28 23:28:46 (GMT)
commit2317f2bf8beb03c60463a9482dbef23540f5c1e0 (patch)
tree9983553e5289cf622e782d0132bb1810276364d2 /src/uscxml
parent7279ab2caf72b68126bf0c1d7e62c7d89024f9a0 (diff)
downloaduscxml-2317f2bf8beb03c60463a9482dbef23540f5c1e0.zip
uscxml-2317f2bf8beb03c60463a9482dbef23540f5c1e0.tar.gz
uscxml-2317f2bf8beb03c60463a9482dbef23540f5c1e0.tar.bz2
Refactoring and W3C tests
- Moved core of interpreter to support various versions - Added experimental setConfiguration() - There can be more than one initial state
Diffstat (limited to 'src/uscxml')
-rw-r--r--src/uscxml/Interpreter.cpp1350
-rw-r--r--src/uscxml/Interpreter.h83
-rw-r--r--src/uscxml/Message.cpp4
-rw-r--r--src/uscxml/interpreter/InterpreterDraft6.cpp920
-rw-r--r--src/uscxml/interpreter/InterpreterDraft6.h31
-rw-r--r--src/uscxml/interpreter/InterpreterDraft7.cpp439
-rw-r--r--src/uscxml/interpreter/InterpreterDraft7.h46
7 files changed, 1601 insertions, 1272 deletions
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index 8e9165e..e59ef5f 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -19,6 +19,8 @@
#include <assert.h>
#include <algorithm>
+#include "uscxml/interpreter/InterpreterDraft6.h"
+
#define VERBOSE 0
namespace uscxml {
@@ -31,7 +33,7 @@ const std::string Interpreter::getUUID() {
return boost::lexical_cast<std::string>(uuidGen());
}
-Interpreter::Interpreter() : Arabica::SAX2DOM::Parser<std::string>() {
+Interpreter::Interpreter() {
_lastRunOnMainThread = 0;
_nsURL = "*";
_thread = NULL;
@@ -51,11 +53,9 @@ Interpreter::Interpreter() : Arabica::SAX2DOM::Parser<std::string>() {
Interpreter* Interpreter::fromDOM(const Arabica::DOM::Node<std::string>& node) {
Arabica::DOM::DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation();
- Interpreter* interpreter = new Interpreter();
+ Interpreter* interpreter = new InterpreterDraft6();
interpreter->_document = domFactory.createDocument("http://www.w3.org/2005/07/scxml", "scxml", 0);
interpreter->_document.appendChild(node);
- interpreter->init();
-
return interpreter;
}
@@ -80,7 +80,6 @@ Interpreter* Interpreter::fromURI(const std::string& uri) {
Interpreter* interpreter = NULL;
- // this is required for windows filenames and does not harm on unices
if (boost::iequals(absUrl.scheme(), "file")) {
Arabica::SAX::InputSource<std::string> inputSource;
inputSource.setSystemId(absUrl.path());
@@ -107,6 +106,59 @@ Interpreter* Interpreter::fromURI(const std::string& uri) {
return interpreter;
}
+Interpreter* Interpreter::fromInputSource(Arabica::SAX::InputSource<std::string>& source) {
+ Interpreter* interpreter = new InterpreterDraft6();
+
+ SCXMLParser* parser = new SCXMLParser(interpreter);
+ if(!parser->parse(source) || !parser->getDocument().hasChildNodes()) {
+ if(parser->_errorHandler.errorsReported()) {
+ LOG(ERROR) << "could not parse input:";
+ LOG(ERROR) << parser->_errorHandler.errors() << std::endl;
+ } else {
+ Arabica::SAX::InputSourceResolver resolver(source, Arabica::default_string_adaptor<std::string>());
+ if (!resolver.resolve()) {
+ LOG(ERROR) << source.getSystemId() << ": no such file";
+ }
+ }
+ delete parser;
+ delete interpreter;
+ return NULL;
+ } else {
+ interpreter->_document = parser->getDocument();
+ }
+ // interpreter->init();
+ delete parser;
+ return interpreter;
+}
+
+SCXMLParser::SCXMLParser(Interpreter* interpreter) : _interpreter(interpreter) {
+ Arabica::SAX::CatchErrorHandler<std::string> errorHandler;
+ setErrorHandler(errorHandler);
+}
+
+void SCXMLParser::startPrefixMapping(const std::string& prefix, const std::string& uri) {
+#if 0
+ std::cout << "starting prefix mapping " << prefix << ": " << uri << std::endl;
+#endif
+ if (boost::iequals(uri, "http://www.w3.org/2005/07/scxml")) {
+ _interpreter->_nsURL = uri;
+ if (prefix.size() == 0) {
+ LOG(INFO) << "Mapped default namespace to 'scxml:'";
+ _interpreter->_xpathPrefix = "scxml:";
+ _interpreter->_nsContext.addNamespaceDeclaration(uri, "scxml");
+ _interpreter->_nsToPrefix[uri] = "scxml";
+ } else {
+ _interpreter->_xpathPrefix = prefix + ":";
+ _interpreter->_xmlNSPrefix = _interpreter->_xpathPrefix;
+ _interpreter->_nsContext.addNamespaceDeclaration(uri, prefix);
+ _interpreter->_nsToPrefix[uri] = prefix;
+ }
+ } else {
+ _interpreter->_nsContext.addNamespaceDeclaration(uri, prefix);
+ _interpreter->_nsToPrefix[uri] = prefix;
+ }
+}
+
void Interpreter::setName(const std::string& name) {
if (!_running) {
_name = name;
@@ -147,83 +199,6 @@ bool Interpreter::toAbsoluteURI(URL& uri) {
return false;
}
-void Interpreter::startPrefixMapping(const std::string& prefix, const std::string& uri) {
-#if 0
- std::cout << "starting prefix mapping " << prefix << ": " << uri << std::endl;
-#endif
- if (boost::iequals(uri, "http://www.w3.org/2005/07/scxml")) {
- _nsURL = uri;
- if (prefix.size() == 0) {
- LOG(INFO) << "Mapped default namespace to 'scxml:'";
- _xpathPrefix = "scxml:";
- _nsContext.addNamespaceDeclaration(uri, "scxml");
- _nsToPrefix[uri] = "scxml";
- } else {
- _xpathPrefix = prefix + ":";
- _xmlNSPrefix = _xpathPrefix;
- _nsContext.addNamespaceDeclaration(uri, prefix);
- _nsToPrefix[uri] = prefix;
- }
- } else {
- _nsContext.addNamespaceDeclaration(uri, prefix);
- _nsToPrefix[uri] = prefix;
- }
-}
-
-Interpreter* Interpreter::fromInputSource(Arabica::SAX::InputSource<std::string>& source) {
- Interpreter* interpreter = new Interpreter();
-
- Arabica::SAX::CatchErrorHandler<std::string> errorHandler;
- interpreter->setErrorHandler(errorHandler);
- if(!interpreter->parse(source) || !interpreter->Arabica::SAX2DOM::Parser<std::string>::getDocument().hasChildNodes()) {
- if(errorHandler.errorsReported()) {
- LOG(ERROR) << "could not parse input:";
- LOG(ERROR) << errorHandler.errors() << std::endl;
- } else {
- Arabica::SAX::InputSourceResolver resolver(source, Arabica::default_string_adaptor<std::string>());
- if (!resolver.resolve()) {
- LOG(ERROR) << source.getSystemId() << ": no such file";
- }
- }
- delete interpreter;
- return NULL;
- } else {
- interpreter->_document = interpreter->Arabica::SAX2DOM::Parser<std::string>::getDocument();
- }
-// interpreter->init();
- return interpreter;
-}
-
-void Interpreter::init() {
- if (_document) {
- NodeList<std::string> scxmls = _document.getElementsByTagNameNS(_nsURL, "scxml");
- if (scxmls.getLength() > 0) {
- _scxml = (Arabica::DOM::Element<std::string>)scxmls.item(0);
-
- // setup xpath and check that it works
- _xpath.setNamespaceContext(_nsContext);
- Arabica::XPath::NodeSet<std::string> scxmls = _xpath.evaluate("/" + _xpathPrefix + "scxml", _document).asNodeSet();
- assert(scxmls.size() > 0);
- assert(scxmls[0] == _scxml);
-
- if (_name.length() == 0)
- _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : getUUID());
-
- normalize(_document);
-
- if (_capabilities & CAN_GENERIC_HTTP)
- _httpServlet = new InterpreterServlet(this);
-
- _sendQueue = new DelayedEventQueue();
- _sendQueue->start();
-
- } else {
- LOG(ERROR) << "Cannot find SCXML element" << std::endl;
- }
- }
- _isInitialized = true;
-}
-
Interpreter::~Interpreter() {
if (_thread) {
_running = false;
@@ -280,140 +255,34 @@ bool Interpreter::runOnMainThread(int fps, bool blocking) {
return (_thread != NULL);
}
-// see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation
-void Interpreter::interpret() {
- if (!_isInitialized)
- init();
-
- if (!_scxml)
- return;
-// dump();
-
- _sessionId = getUUID();
-
- std::string datamodelName;
- if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel"))
- datamodelName = ATTR(_scxml, "datamodel");
- if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "profile")) // SCION SCXML uses profile to specify datamodel
- datamodelName = ATTR(_scxml, "profile");
- _dataModel = Factory::createDataModel(datamodelName, this);
- if(datamodelName.length() > 0 && !_dataModel) {
- LOG(ERROR) << "No datamodel for " << datamodelName << " registered";
- }
-
- if (_dataModel) {
- _dataModel.assign("_x.args", _cmdLineOptions);
- if (_httpServlet) {
- Data data;
- data.compound["location"] = Data(_httpServlet->getURL(), Data::VERBATIM);
- _dataModel.assign("_ioprocessors['http']", data);
- }
- }
-
- setupIOProcessors();
-
- _running = true;
- _binding = (HAS_ATTR(_scxml, "binding") && boost::iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY);
-
- // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding
-
- if (_dataModel && _binding == EARLY) {
- // initialize all data elements
- NodeSet<std::string> dataElems = _xpath.evaluate("//" + _xpathPrefix + "data", _document).asNodeSet();
- for (unsigned int i = 0; i < dataElems.size(); i++) {
- initializeData(dataElems[i]);
- }
- } else if(_dataModel) {
- // initialize current data elements
- NodeSet<std::string> topDataElems = filterChildElements(_xmlNSPrefix + "data", filterChildElements(_xmlNSPrefix + "datamodel", _scxml));
- for (unsigned int i = 0; i < topDataElems.size(); i++) {
- initializeData(topDataElems[i]);
- }
- }
-
- // executeGlobalScriptElements
- NodeSet<std::string> globalScriptElems = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "script", _document).asNodeSet();
- for (unsigned int i = 0; i < globalScriptElems.size(); i++) {
- if (_dataModel)
- executeContent(globalScriptElems[i]);
- }
-
- // initial transition might be implict
- NodeSet<std::string> initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _document).asNodeSet();
- if (initialTransitions.size() == 0) {
- Arabica::DOM::Element<std::string> initialState = (Arabica::DOM::Element<std::string>)getInitialState();
- Arabica::DOM::Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial");
- initialElem.setAttribute("generated", "true");
- Arabica::DOM::Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition");
- transitionElem.setAttribute("target", initialState.getAttribute("id"));
- initialElem.appendChild(transitionElem);
- _scxml.appendChild(initialElem);
- initialTransitions.push_back(transitionElem);
- }
- enterStates(initialTransitions);
-
-// assert(hasLegalConfiguration());
- mainEventLoop();
-
- if (_parentQueue) {
- // send one final event to unblock eventual listeners
- Event quit;
- quit.name = "done.state.scxml";
- _parentQueue->push(quit);
- }
-
- // set datamodel to null from this thread
- if(_dataModel)
- _dataModel = DataModel();
-
-}
-
-/**
- * Called with a single data element from the topmost datamodel element.
- */
-void Interpreter::initializeData(const Arabica::DOM::Node<std::string>& data) {
- if (!_dataModel) {
- LOG(ERROR) << "Cannot initialize data when no datamodel is given!";
- return;
- }
- try {
- if (!HAS_ATTR(data, "id")) {
- LOG(ERROR) << "Data element has no id!";
- return;
- }
-
- if (HAS_ATTR(data, "expr")) {
- std::string value = ATTR(data, "expr");
- _dataModel.assign(ATTR(data, "id"), value);
- } else if (HAS_ATTR(data, "src")) {
- URL srcURL(ATTR(data, "src"));
- if (!srcURL.isAbsolute())
- toAbsoluteURI(srcURL);
-
- std::stringstream ss;
- if (_cachedURLs.find(srcURL.asString()) != _cachedURLs.end()) {
- ss << _cachedURLs[srcURL.asString()];
- } else {
- ss << srcURL;
- _cachedURLs[srcURL.asString()] = srcURL;
- }
- _dataModel.assign(ATTR(data, "id"), ss.str());
-
- } else if (data.hasChildNodes()) {
- // search for the text node with the actual script
- NodeList<std::string> dataChilds = data.getChildNodes();
- for (int i = 0; i < dataChilds.getLength(); i++) {
- if (dataChilds.item(i).getNodeType() == Node_base::TEXT_NODE) {
- Data value = Data(dataChilds.item(i).getNodeValue());
- _dataModel.assign(ATTR(data, "id"), value);
- break;
- }
- }
+void Interpreter::init() {
+ if (_document) {
+ NodeList<std::string> scxmls = _document.getElementsByTagNameNS(_nsURL, "scxml");
+ if (scxmls.getLength() > 0) {
+ _scxml = (Arabica::DOM::Element<std::string>)scxmls.item(0);
+
+ // setup xpath and check that it works
+ _xpath.setNamespaceContext(_nsContext);
+ Arabica::XPath::NodeSet<std::string> scxmls = _xpath.evaluate("/" + _xpathPrefix + "scxml", _document).asNodeSet();
+ assert(scxmls.size() > 0);
+ assert(scxmls[0] == _scxml);
+
+ if (_name.length() == 0)
+ _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : getUUID());
+
+ normalize(_document);
+
+ if (_capabilities & CAN_GENERIC_HTTP)
+ _httpServlet = new InterpreterServlet(this);
+
+ _sendQueue = new DelayedEventQueue();
+ _sendQueue->start();
+
+ } else {
+ LOG(ERROR) << "Cannot find SCXML element" << std::endl;
}
-
- } catch (Event e) {
- LOG(ERROR) << "Syntax error in data element:" << std::endl << e << std::endl;
}
+ _isInitialized = true;
}
void Interpreter::normalize(const Arabica::DOM::Document<std::string>& node) {
@@ -476,176 +345,6 @@ void Interpreter::normalize(const Arabica::DOM::Document<std::string>& node) {
#endif
}
-void Interpreter::mainEventLoop() {
- std::set<InterpreterMonitor*>::iterator monIter;
-
- while(_running) {
- NodeSet<std::string> enabledTransitions;
- _stable = false;
-
- // Here we handle eventless transitions and transitions
- // triggered by internal events until machine is stable
- while(_running && !_stable) {
-#if 0
- std::cout << "Configuration: ";
- for (int i = 0; i < _configuration.size(); i++) {
- std::cout << ATTR(_configuration[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
- monIter = _monitors.begin();
- while(monIter != _monitors.end()) {
- try {
- (*monIter)->beforeMicroStep(this);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling beforeMicroStep on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling beforeMicroStep on monitors";
- }
- monIter++;
- }
-
- enabledTransitions = selectEventlessTransitions();
- if (enabledTransitions.size() == 0) {
- if (_internalQueue.size() == 0) {
- _stable = true;
- } else {
- _currEvent = _internalQueue.front();
- _internalQueue.pop_front();
-#if VERBOSE
- std::cout << "Received internal event " << _currEvent.name << std::endl;
-#endif
- if (_dataModel)
- _dataModel.setEvent(_currEvent);
- enabledTransitions = selectTransitions(_currEvent.name);
- }
- }
- if (!enabledTransitions.empty()) {
- monIter = _monitors.begin();
- while(monIter != _monitors.end()) {
- try {
- (*monIter)->beforeTakingTransitions(this, enabledTransitions);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling beforeTakingTransitions on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling beforeTakingTransitions on monitors";
- }
- monIter++;
- }
- microstep(enabledTransitions);
- }
- }
-
- for (unsigned int i = 0; i < _statesToInvoke.size(); i++) {
- NodeSet<std::string> invokes = filterChildElements(_xmlNSPrefix + "invoke", _statesToInvoke[i]);
- for (unsigned int j = 0; j < invokes.size(); j++) {
- invoke(invokes[j]);
- }
- }
-
- _statesToInvoke = NodeSet<std::string>();
- if (!_internalQueue.empty())
- continue;
-
- // assume that we have a legal configuration as soon as the internal queue is empty
-// assert(hasLegalConfiguration());
-
- monIter = _monitors.begin();
-// if (!_sendQueue || _sendQueue->isEmpty()) {
- while(monIter != _monitors.end()) {
- try {
- (*monIter)->onStableConfiguration(this);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling onStableConfiguration on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling onStableConfiguration on monitors";
- }
- monIter++;
- }
-// }
-
- // whenever we have a stable configuration, run the mainThread hooks with 200fps
- while(_externalQueue.isEmpty() && _thread == NULL) {
- runOnMainThread(200);
- }
-
- _currEvent = _externalQueue.pop();
-#if VERBOSE
- std::cout << "Received externalEvent event " << _currEvent.name << std::endl;
-#endif
- _currEvent.type = Event::EXTERNAL; // make sure it is set to external
- if (!_running)
- exitInterpreter();
-
- if (_dataModel && boost::iequals(_currEvent.name, "cancel.invoke." + _sessionId))
- break;
-
- if (_dataModel)
- try {
- _dataModel.setEvent(_currEvent);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl;
- }
- for (unsigned int i = 0; i < _configuration.size(); i++) {
- NodeSet<std::string> invokes = filterChildElements(_xmlNSPrefix + "invoke", _configuration[i]);
- for (unsigned int j = 0; j < invokes.size(); j++) {
- Arabica::DOM::Element<std::string> invokeElem = (Arabica::DOM::Element<std::string>)invokes[j];
- std::string invokeId;
- if (HAS_ATTR(invokeElem, "id"))
- invokeId = ATTR(invokeElem, "id");
- if (HAS_ATTR(invokeElem, "idlocation") && _dataModel)
- invokeId = _dataModel.evalAsString(ATTR(invokeElem, "idlocation"));
-
- std::string autoForward = invokeElem.getAttribute("autoforward");
- if (boost::iequals(invokeId, _currEvent.invokeid)) {
-
- Arabica::XPath::NodeSet<std::string> finalizes = filterChildElements(_xmlNSPrefix + "finalize", invokeElem);
- for (int k = 0; k < finalizes.size(); k++) {
- Arabica::DOM::Element<std::string> finalizeElem = Arabica::DOM::Element<std::string>(finalizes[k]);
- executeContent(finalizeElem);
- }
-
- }
- if (boost::iequals(autoForward, "true")) {
- try {
- _invokers[invokeId].send(_currEvent);
- } catch(...) {
- LOG(ERROR) << "Exception caught while sending event to invoker " << invokeId;
- }
- }
- }
- }
- enabledTransitions = selectTransitions(_currEvent.name);
- if (!enabledTransitions.empty())
- microstep(enabledTransitions);
- }
- monIter = _monitors.begin();
- while(monIter != _monitors.end()) {
- try {
- (*monIter)->beforeCompletion(this);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling beforeCompletion on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling beforeCompletion on monitors";
- }
- monIter++;
- }
-
- exitInterpreter();
-
- monIter = _monitors.begin();
- while(monIter != _monitors.end()) {
- try {
- (*monIter)->afterCompletion(this);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling afterCompletion on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling afterCompletion on monitors";
- }
- monIter++;
- }
-
-}
void Interpreter::internalDoneSend(const Arabica::DOM::Node<std::string>& state) {
if (!isState(state))
@@ -1075,55 +774,6 @@ void Interpreter::cancelInvoke(const Arabica::DOM::Node<std::string>& element) {
}
}
-Arabica::XPath::NodeSet<std::string> Interpreter::selectTransitions(const std::string& event) {
- Arabica::XPath::NodeSet<std::string> enabledTransitions;
-
- NodeSet<std::string> atomicStates;
- for (unsigned int i = 0; i < _configuration.size(); i++) {
- if (isAtomic(_configuration[i]))
- atomicStates.push_back(_configuration[i]);
- }
- atomicStates.to_document_order();
-
- for (unsigned int i = 0; i < atomicStates.size(); i++) {
- NodeSet<std::string> ancestors = getProperAncestors(atomicStates[i], Arabica::DOM::Node<std::string>());
-
- NodeSet<std::string> sortedAncestors;
- sortedAncestors.push_back(atomicStates[i]);
- sortedAncestors.insert(sortedAncestors.end(), ancestors.begin(), ancestors.end());
-
- for (unsigned int j = 0; j < sortedAncestors.size(); j++) {
- NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", sortedAncestors[j]);
- for (unsigned int k = 0; k < transitions.size(); k++) {
- std::string eventName;
- if (HAS_ATTR(transitions[k], "event")) {
- eventName = ATTR(transitions[k], "event");
- } else if(HAS_ATTR(transitions[k], "eventexpr")) {
- if (_dataModel) {
- eventName = _dataModel.evalAsString(ATTR(transitions[k], "eventexpr"));
- } else {
- LOG(ERROR) << "Transition has eventexpr attribute with no datamodel defined";
- goto LOOP;
- }
- } else {
- goto LOOP;
- }
-
- if (eventName.length() > 0 &&
- nameMatch(eventName, event) &&
- hasConditionMatch(transitions[k])) {
- enabledTransitions.push_back(transitions[k]);
- goto LOOP;
- }
- }
- }
-LOOP:
- ;
- }
-
- enabledTransitions = filterPreempted(enabledTransitions);
- return enabledTransitions;
-}
// see: http://www.w3.org/TR/scxml/#EventDescriptors
bool Interpreter::nameMatch(const std::string& transitionEvent, const std::string& event) {
@@ -1167,49 +817,10 @@ bool Interpreter::nameMatch(const std::string& transitionEvent, const std::strin
return false;
}
-Arabica::XPath::NodeSet<std::string> Interpreter::selectEventlessTransitions() {
- Arabica::XPath::NodeSet<std::string> enabledTransitions;
-
- NodeSet<std::string> atomicStates;
- for (unsigned int i = 0; i < _configuration.size(); i++) {
- if (isAtomic(_configuration[i]))
- atomicStates.push_back(_configuration[i]);
- }
- atomicStates.to_document_order();
-
- for (unsigned int i = 0; i < atomicStates.size(); i++) {
- NodeSet<std::string> ancestors = getProperAncestors(atomicStates[i], Arabica::DOM::Node<std::string>());
- ancestors.push_back(atomicStates[i]);
- for (unsigned int j = 0; j < ancestors.size(); j++) {
- NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", ancestors[j]);
- for (unsigned int k = 0; k < transitions.size(); k++) {
- if (!HAS_ATTR(transitions[k], "event") && hasConditionMatch(transitions[k])) {
- enabledTransitions.push_back(transitions[k]);
- goto LOOP;
- }
- }
-
-#if 0
- NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", ancestors[j]);
- for (unsigned int k = 0; k < transitions.size(); k++) {
- if (!((Arabica::DOM::Element<std::string>)transitions[k]).hasAttribute("event") && hasConditionMatch(transitions[k])) {
- enabledTransitions.push_back(transitions[k]);
- goto LOOP;
- }
- }
-#endif
- }
-LOOP:
- ;
- }
-
- enabledTransitions = filterPreempted(enabledTransitions);
- return enabledTransitions;
-}
bool Interpreter::hasConditionMatch(const Arabica::DOM::Node<std::string>& conditional) {
- if (HAS_ATTR(conditional, "cond")) {
+ if (HAS_ATTR(conditional, "cond") && ATTR(conditional, "cond").length() > 0) {
if (!_dataModel) {
LOG(ERROR) << "Cannot check a condition without a datamodel";
return false;
@@ -1224,129 +835,6 @@ bool Interpreter::hasConditionMatch(const Arabica::DOM::Node<std::string>& condi
return true; // no condition is always true
}
-Arabica::XPath::NodeSet<std::string> Interpreter::filterPreempted(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- Arabica::XPath::NodeSet<std::string> filteredTransitions;
- for (unsigned int i = 0; i < enabledTransitions.size(); i++) {
- Arabica::DOM::Node<std::string> t = enabledTransitions[i];
- for (unsigned int j = i+1; j < enabledTransitions.size(); j++) {
- Arabica::DOM::Node<std::string> t2 = enabledTransitions[j];
- if (isPreemptingTransition(t2, t)) {
-#if VERBOSE
- std::cout << "Transition preempted!: " << std::endl << t2 << std::endl << t << std::endl;
-#endif
- goto LOOP;
- }
- }
- filteredTransitions.push_back(t);
-LOOP:
- ;
- }
- return filteredTransitions;
-}
-
-bool Interpreter::isPreemptingTransition(const Arabica::DOM::Node<std::string>& t1, const Arabica::DOM::Node<std::string>& t2) {
- assert(t1);
- assert(t2);
-
-#if VERBOSE
- std::cout << "Checking preemption: " << std::endl << t1 << std::endl << t2 << std::endl;
-#endif
-
-#if 1
- if (t1 == t2)
- return false;
- if (isWithinSameChild(t1) && (!isTargetless(t2) && !isWithinSameChild(t2)))
- return true;
- if (!isTargetless(t1) && !isWithinSameChild(t1))
- return true;
- return false;
-#endif
-
-#if 0
- // isPreempted from chris nuernberger
- if (isTargetless(t1))
- return false;
-
- Arabica::DOM::Node<std::string> existingRoot = getTransitionSubgraph(t1);
- Arabica::DOM::Node<std::string> nextRoot = getTransitionSubgraph(t2);
-
- if (existingRoot == nextRoot || isDescendant(existingRoot, nextRoot) || isDescendant(nextRoot, existingRoot))
- return true;
-
- return false;
-#endif
-}
-
-/**
- * filterPreempted approach from chris nuernberger
- */
-#if 0
-Arabica::DOM::Node<std::string> Interpreter::getTransitionSubgraph(const Arabica::DOM::Node<std::string>& transition) {
- Arabica::XPath::NodeSet<std::string> targets = getTargetStates(transition);
- Arabica::DOM::Node<std::string> source = getSourceState(transition);
-
- if (!targets.size() == 0)
- return source;
-
- if (boost::iequals(ATTR(transition, "type"), "internal") && isCompound(source)) {
- bool allDescendants = true;
- for (int i = 0; i < targets.size(); i++) {
- if (!isDescendant(targets[i], source)) {
- allDescendants = false;
- break;
- }
- }
- if (allDescendants)
- return source;
- }
-
- targets.push_back(source);
- return findLCCA(targets);
-}
-#endif
-
-void Interpreter::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
-#if 0
- std::cout << "Transitions: ";
- for (int i = 0; i < enabledTransitions.size(); i++) {
- std::cout << ((Arabica::DOM::Element<std::string>)getSourceState(enabledTransitions[i])).getAttribute("id") << " -> " << std::endl;
- NodeSet<std::string> targetSet = getTargetStates(enabledTransitions[i]);
- for (int j = 0; j < targetSet.size(); j++) {
- std::cout << " " << ((Arabica::DOM::Element<std::string>)targetSet[j]).getAttribute("id") << std::endl;
- }
- }
- std::cout << std::endl;
-#endif
-
- exitStates(enabledTransitions);
- executeTransitionContent(enabledTransitions);
- enterStates(enabledTransitions);
-}
-
-void Interpreter::exitInterpreter() {
- NodeSet<std::string> statesToExit = _configuration;
- statesToExit.to_document_order();
- statesToExit.reverse();
-
- for (int i = 0; i < statesToExit.size(); i++) {
- Arabica::XPath::NodeSet<std::string> onExitElems = filterChildElements(_xmlNSPrefix + "onexit", statesToExit[i]);
- for (int j = 0; j < onExitElems.size(); j++) {
- executeContent(onExitElems[j]);
- }
- Arabica::XPath::NodeSet<std::string> invokeElems = filterChildElements(_xmlNSPrefix + "invoke", statesToExit[i]);
- for (int j = 0; j < invokeElems.size(); j++) {
- cancelInvoke(invokeElems[j]);
- }
- if (isFinal(statesToExit[i]) && parentIsScxmlState(statesToExit[i])) {
- returnDoneEvent(statesToExit[i]);
- }
- }
- _configuration = NodeSet<std::string>();
-}
-
-void Interpreter::executeTransitionContent(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- executeContent(enabledTransitions);
-}
void Interpreter::executeContent(const NodeList<std::string>& content) {
for (unsigned int i = 0; i < content.getLength(); i++) {
@@ -1392,40 +880,32 @@ void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content)
if (HAS_ATTR(ifElem, "cond"))
std::cout << ATTR(ifElem, "cond") << std::endl;
#endif
- if(hasConditionMatch(ifElem)) {
- // condition is true, execute all content up to an elseif, else or end
- if (ifElem.hasChildNodes()) {
- NodeList<std::string> childs = ifElem.getChildNodes();
- for (unsigned int i = 0; i < childs.getLength(); i++) {
- if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE)
- continue;
- if (boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "elseif") ||
- boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "else"))
+ /**
+ * A block is everything up to or between elseifs and else. Those element
+ * determine whether the block is true and its executable content executed.
+ */
+ if (ifElem.hasChildNodes()) {
+ bool blockIsTrue = hasConditionMatch(ifElem);
+ NodeList<std::string> childs = ifElem.getChildNodes();
+ for (unsigned int i = 0; i < childs.getLength(); i++) {
+ if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE)
+ continue;
+ if (boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "elseif") ||
+ boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "else")) {
+ if (blockIsTrue) {
+ // last block was true, break here
break;
- executeContent(childs.item(i));
- }
- }
- } else {
- // condition does not match - do we have an elsif?
- if (ifElem.hasChildNodes()) {
- NodeSet<std::string> elseifElem = filterChildElements(_xmlNSPrefix + "elseif", ifElem);
- for (unsigned int i = 0; i < elseifElem.size(); i++) {
-#if 0
- if (HAS_ATTR(elseifElem[i], "cond"))
- std::cout << ATTR(elseifElem[i], "cond") << std::endl;
-#endif
- if (hasConditionMatch(elseifElem[i])) {
- executeContent(elseifElem[i].getChildNodes());
- goto ELSIF_ELEM_MATCH;
+ } else {
+ // is this block the one to execute?
+ blockIsTrue = hasConditionMatch(childs.item(i));
+ }
+ } else {
+ if (blockIsTrue) {
+ executeContent(childs.item(i));
}
}
- NodeSet<std::string> elseElem = filterChildElements(_xmlNSPrefix + "else", ifElem);
- if (elseElem.size() > 0)
- executeContent(elseElem[0].getChildNodes());
}
}
-ELSIF_ELEM_MATCH:
- ;
} else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "elseif")) {
std::cerr << "Found single elsif to evaluate!" << std::endl;
} else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "else")) {
@@ -1578,589 +1058,6 @@ ELSIF_ELEM_MATCH:
void Interpreter::returnDoneEvent(const Arabica::DOM::Node<std::string>& state) {
}
-void Interpreter::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- NodeSet<std::string> statesToExit;
- std::set<InterpreterMonitor*>::iterator monIter;
-
-#if VERBOSE
- std::cout << "Enabled exit transitions: " << std::endl;
- for (int i = 0; i < enabledTransitions.size(); i++) {
- std::cout << enabledTransitions[i] << std::endl;
- }
- std::cout << std::endl;
-#endif
-
- for (int i = 0; i < enabledTransitions.size(); i++) {
- Arabica::DOM::Element<std::string> transition = ((Arabica::DOM::Element<std::string>)enabledTransitions[i]);
- if (!isTargetless(transition)) {
- std::string transitionType = (boost::iequals(transition.getAttribute("type"), "internal") ? "internal" : "external");
- NodeSet<std::string> tStates = getTargetStates(transition);
- Arabica::DOM::Node<std::string> ancestor;
- Arabica::DOM::Node<std::string> source = getSourceState(transition);
-
- bool allDescendants = true;
- for (int j = 0; j < tStates.size(); j++) {
- if (!isDescendant(tStates[j], source)) {
- allDescendants = false;
- break;
- }
- }
- if (boost::iequals(transitionType, "internal") &&
- isCompound(source) &&
- allDescendants) {
- ancestor = source;
- } else {
- NodeSet<std::string> tmpStates;
- tmpStates.push_back(source);
- tmpStates.insert(tmpStates.end(), tStates.begin(), tStates.end());
-
-#if VERBOSE
- std::cout << "tmpStates: ";
- for (int i = 0; i < tmpStates.size(); i++) {
- std::cout << ATTR(tmpStates[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
- ancestor = findLCCA(tmpStates);
- }
-
-#if VERBOSE
- std::cout << "Ancestor: " << ATTR(ancestor, "id") << std::endl;;
-#endif
-
- for (int j = 0; j < _configuration.size(); j++) {
- if (isDescendant(_configuration[j], ancestor))
- statesToExit.push_back(_configuration[j]);
- }
- }
- }
-
-#if VERBOSE
- std::cout << "States to exit: ";
- for (int i = 0; i < statesToExit.size(); i++) {
- std::cout << LOCALNAME(statesToExit[i]) << ":" << ATTR(statesToExit[i], "id") << ", ";
- }
- std::cout << std::endl;
-
-#endif
-
- // remove statesToExit from _statesToInvoke
- std::list<Arabica::DOM::Node<std::string> > tmp;
- for (int 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.to_document_order();
- statesToExit.reverse();
-
- monIter = _monitors.begin();
- while(monIter != _monitors.end()) {
- try {
- (*monIter)->beforeExitingStates(this, statesToExit);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling beforeExitingStates on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling beforeExitingStates on monitors";
- }
- monIter++;
- }
-
- for (int i = 0; i < statesToExit.size(); i++) {
- NodeSet<std::string> histories = filterChildElements(_xmlNSPrefix + "history", statesToExit[i]);
- for (int j = 0; j < histories.size(); j++) {
- Arabica::DOM::Element<std::string> historyElem = (Arabica::DOM::Element<std::string>)histories[j];
- std::string historyType = (historyElem.hasAttribute("type") ? historyElem.getAttribute("type") : "shallow");
- NodeSet<std::string> historyNodes;
- for (int k = 0; k < _configuration.size(); k++) {
- if (boost::iequals(historyType, "deep")) {
- if (isAtomic(_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 0
- std::cout << "History node " << ATTR(historyElem, "id") << " contains: ";
- for (int i = 0; i < historyNodes.size(); i++) {
- std::cout << ATTR(historyNodes[i], "id") << ", ";
- }
- std::cout << std::endl;
-
-#endif
-
- }
- }
-
- for (int i = 0; i < statesToExit.size(); i++) {
- NodeSet<std::string> onExits = filterChildElements(_xmlNSPrefix + "onExit", statesToExit[i]);
- for (int j = 0; j < onExits.size(); j++) {
- Arabica::DOM::Element<std::string> onExitElem = (Arabica::DOM::Element<std::string>)onExits[j];
- executeContent(onExitElem);
- }
- NodeSet<std::string> invokes = filterChildElements(_xmlNSPrefix + "invoke", statesToExit[i]);
- for (int j = 0; j < invokes.size(); j++) {
- Arabica::DOM::Element<std::string> invokeElem = (Arabica::DOM::Element<std::string>)invokes[j];
- cancelInvoke(invokeElem);
- }
- }
-
- // remove statesToExit from _configuration
- tmp.clear();
- for (int i = 0; i < _configuration.size(); i++) {
- if (!isMember(_configuration[i], statesToExit)) {
- tmp.push_back(_configuration[i]);
- }
- }
- _configuration = NodeSet<std::string>();
- _configuration.insert(_configuration.end(), tmp.begin(), tmp.end());
-
- monIter = _monitors.begin();
- while(monIter != _monitors.end()) {
- try {
- (*monIter)->afterExitingStates(this);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling afterExitingStates on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling afterExitingStates on monitors";
- }
- monIter++;
- }
-
-}
-
-#ifdef ORIG_ENTERSTATES
-void Interpreter::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- NodeSet<std::string> statesToEnter;
- NodeSet<std::string> statesForDefaultEntry;
- std::set<InterpreterMonitor*>::iterator monIter;
-
-#if VERBOSE
- std::cout << "Enabled enter transitions: " << std::endl;
- for (int i = 0; i < enabledTransitions.size(); i++) {
- std::cout << enabledTransitions[i] << std::endl;
- }
- std::cout << std::endl;
-#endif
-
- for (int i = 0; i < enabledTransitions.size(); i++) {
- Arabica::DOM::Element<std::string> transition = ((Arabica::DOM::Element<std::string>)enabledTransitions[i]);
- if (!isTargetless(transition)) {
- std::string transitionType = (boost::iequals(transition.getAttribute("type"), "internal") ? "internal" : "external");
- NodeSet<std::string> tStates = getTargetStates(transition);
-
-#if VERBOSE
- std::cout << "Target States: ";
- for (int i = 0; i < tStates.size(); i++) {
- std::cout << ATTR(tStates[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- Arabica::DOM::Node<std::string> ancestor;
- Arabica::DOM::Node<std::string> source = getSourceState(transition);
-#if VERBOSE
- std::cout << "Source States: " << ATTR(source, "id") << std::endl;
-#endif
- assert(source);
-
- bool allDescendants = true;
- for (int j = 0; j < tStates.size(); j++) {
- if (!isDescendant(tStates[j], source)) {
- allDescendants = false;
- break;
- }
- }
- if (boost::iequals(transitionType, "internal") &&
- isCompound(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 << "Ancestor: " << ATTR(ancestor, "id") << std::endl;
-#endif
-
- for (int j = 0; j < tStates.size(); j++) {
- addStatesToEnter(tStates[j], statesToEnter, statesForDefaultEntry);
- }
-
-#if VERBOSE
- std::cout << "States to enter: ";
- for (int i = 0; i < statesToEnter.size(); i++) {
- std::cout << LOCALNAME(statesToEnter[i]) << ":" << ATTR(statesToEnter[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- for (int j = 0; j < tStates.size(); j++) {
- NodeSet<std::string> ancestors = getProperAncestors(tStates[j], ancestor);
-
-#if VERBOSE
- std::cout << "Proper Ancestors of " << ATTR(tStates[j], "id") << " and " << ATTR(ancestor, "id") << ": ";
- for (int i = 0; i < ancestors.size(); i++) {
- std::cout << ATTR(ancestors[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- for (int k = 0; k < ancestors.size(); k++) {
- statesToEnter.push_back(ancestors[k]);
- if(isParallel(ancestors[k])) {
- NodeSet<std::string> childs = getChildStates(ancestors[k]);
- for (int l = 0; l < childs.size(); l++) {
- bool someIsDescendant = false;
- for (int m = 0; m < statesToEnter.size(); m++) {
- if (isDescendant(statesToEnter[m], childs[l])) {
- someIsDescendant = true;
- break;
- }
- }
- if (!someIsDescendant) {
- addStatesToEnter(childs[l], statesToEnter, statesForDefaultEntry);
- }
- }
- }
- }
- }
- }
- }
- statesToEnter.to_document_order();
-
- monIter = _monitors.begin();
- while(monIter != _monitors.end()) {
- try {
- (*monIter)->beforeEnteringStates(this, statesToEnter);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling beforeEnteringStates on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling beforeEnteringStates on monitors";
- }
- monIter++;
- }
-
- for (int i = 0; i < statesToEnter.size(); i++) {
- Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)statesToEnter[i];
- _configuration.push_back(stateElem);
- _statesToInvoke.push_back(stateElem);
- if (_binding == LATE && stateElem.getAttribute("isFirstEntry").size() > 0) {
- NodeSet<std::string> dataModelElems = filterChildElements(_xmlNSPrefix + "datamodel", stateElem);
- if(dataModelElems.size() > 0 && _dataModel) {
- Arabica::XPath::NodeSet<std::string> dataElems = filterChildElements(_xmlNSPrefix + "data", dataModelElems[0]);
- for (int j = 0; j < dataElems.size(); j++) {
- initializeData(dataElems[j]);
- }
- }
- stateElem.setAttribute("isFirstEntry", "");
- }
- // execute onentry executable content
- NodeSet<std::string> onEntryElems = filterChildElements(_xmlNSPrefix + "onEntry", stateElem);
- executeContent(onEntryElems);
-
- if (isMember(stateElem, statesForDefaultEntry)) {
- // execute initial transition content for compund states
- Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", stateElem).asNodeSet();
- for (int j = 0; j < transitions.size(); j++) {
- executeContent(transitions[j]);
- }
- }
-
- if (isFinal(stateElem)) {
- internalDoneSend(stateElem);
- Arabica::DOM::Element<std::string> parent = (Arabica::DOM::Element<std::string>)stateElem.getParentNode();
-
- if (isParallel(parent.getParentNode())) {
- Arabica::DOM::Element<std::string> grandParent = (Arabica::DOM::Element<std::string>)parent.getParentNode();
-
- Arabica::XPath::NodeSet<std::string> childs = getChildStates(grandParent);
- bool inFinalState = true;
- for (int j = 0; j < childs.size(); j++) {
- if (!isInFinalState(childs[j])) {
- inFinalState = false;
- break;
- }
- }
- if (inFinalState) {
- internalDoneSend(parent);
- }
- }
- }
- }
- for (int i = 0; i < _configuration.size(); i++) {
- Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)_configuration[i];
- if (isFinal(stateElem) && parentIsScxmlState(stateElem)) {
- _running = false;
- _done = true;
- }
- }
-
- monIter = _monitors.begin();
- while(monIter != _monitors.end()) {
- try {
- (*monIter)->afterEnteringStates(this);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error when calling afterEnteringStates on monitors: " << std::endl << e << std::endl;
- } catch (...) {
- LOG(ERROR) << "An exception occured when calling afterEnteringStates on monitors";
- }
- monIter++;
- }
-
-}
-
-void Interpreter::addStatesToEnter(const Arabica::DOM::Node<std::string>& state,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) {
- std::string stateId = ((Arabica::DOM::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 (int i = 0; i < historyValue.size(); i++) {
- std::cout << ATTR(historyValue[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- for (int i = 0; i < historyValue.size(); i++) {
- addStatesToEnter(historyValue[i], statesToEnter, statesForDefaultEntry);
- NodeSet<std::string> ancestors = getProperAncestors(historyValue[i], state);
-
-#if VERBOSE
- std::cout << "Proper Ancestors: ";
- for (int i = 0; i < ancestors.size(); i++) {
- std::cout << ATTR(ancestors[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- for (int j = 0; j < ancestors.size(); j++) {
- statesToEnter.push_back(ancestors[j]);
- }
- }
- } else {
- NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", state);
- for (int i = 0; i < transitions.size(); i++) {
- NodeSet<std::string> targets = getTargetStates(transitions[i]);
- for (int j = 0; j < targets.size(); j++) {
- addStatesToEnter(targets[j], statesToEnter, statesForDefaultEntry);
-
- // Modifications from chris nuernberger
- NodeSet<std::string> ancestors = getProperAncestors(targets[j], state);
- for (int k = 0; k < ancestors.size(); k++) {
- statesToEnter.push_back(ancestors[k]);
- }
- }
- }
- }
- } else {
- statesToEnter.push_back(state);
- if (isCompound(state)) {
- statesForDefaultEntry.push_back(state);
-
- addStatesToEnter(getInitialState(state), statesToEnter, statesForDefaultEntry);
-
-# if 0
- NodeSet<std::string> tStates = getTargetStates(getInitialState(state));
- for (int i = 0; i < tStates.size(); i++) {
- addStatesToEnter(tStates[i], statesToEnter, statesForDefaultEntry);
- }
-# endif
- // addStatesToEnter(getInitialState(state), statesToEnter, statesForDefaultEntry);
- // NodeSet<std::string> tStates = getTargetStates(getInitialState(state));
-
- } else if(isParallel(state)) {
- NodeSet<std::string> childStates = getChildStates(state);
- for (int i = 0; i < childStates.size(); i++) {
- addStatesToEnter(childStates[i], statesToEnter, statesForDefaultEntry);
- }
- }
- }
-}
-#endif
-
-#ifdef ENTERSTATES_02_2013
-void Interpreter::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- NodeSet<std::string> statesToEnter;
- NodeSet<std::string> statesForDefaultEntry;
- std::set<InterpreterMonitor*>::iterator monIter;
-
- computeEntrySet(enabledTransitions, statesToEnter, statesForDefaultEntry);
- statesToEnter.sort(); // entry order is document order
- for (int i = 0; i < statesToEnter.size(); i++) {
- Arabica::DOM::Element<std::string> s = (Arabica::DOM::Element<std::string>)statesToEnter[i];
- _configuration.push_back(s);
- _statesToInvoke.push_back(s);
-
- if (_binding == LATE && ATTR(s, "isFirstEntry").size() > 0) {
- NodeSet<std::string> dataModelElems = filterChildElements(_xmlNSPrefix + "datamodel", s);
- if(dataModelElems.size() > 0 && _dataModel) {
- Arabica::XPath::NodeSet<std::string> dataElems = filterChildElements(_xmlNSPrefix + "data", dataModelElems[0]);
- for (int j = 0; j < dataElems.size(); j++) {
- initializeData(dataElems[j]);
- }
- }
- s.setAttribute("isFirstEntry", "");
- }
- executeContent(filterChildElements(_xmlNSPrefix + "onEntry", s));
- if (isMember(s, statesForDefaultEntry)) {
- executeContent(getInitialState(s)); // TODO: This part is unclear
- }
-
-#if VERBOSE
- std::cout << "Is state " << ATTR(s, "id") << " final?";
-#endif
- if (isFinal(s)) {
- if (parentIsScxmlState(s)) {
- _running = false;
- _done = true;
- } else {
- Arabica::DOM::Element<std::string> parent = (Arabica::DOM::Element<std::string>)s.getParentNode();
- Arabica::DOM::Element<std::string> grandParent = (Arabica::DOM::Element<std::string>)parent.getParentNode();
- internalDoneSend(parent);
-
- if (isParallel(grandParent)) {
- Arabica::XPath::NodeSet<std::string> childs = getChildStates(grandParent);
- bool inFinalState = true;
- for (int j = 0; j < childs.size(); j++) {
- if (!isInFinalState(childs[j])) {
- inFinalState = false;
- break;
- }
- }
- if (inFinalState) {
- internalDoneSend(grandParent);
- }
- }
- }
- }
- }
-}
-void Interpreter::computeEntrySet(const Arabica::XPath::NodeSet<std::string>& transitions,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) {
- for (int i = 0; i < transitions.size(); i++) {
- NodeSet<std::string> targets = getTargetStates(transitions[i]);
- for (int j = 0; j < targets.size(); j++) {
- statesToEnter.push_back(targets[i]);
- }
- }
- for (int i = 0; i < transitions.size(); i++) {
- Arabica::DOM::Node<std::string> ancestor = getTransitionDomain(transitions[i]);
- NodeSet<std::string> targets = getTargetStates(transitions[i]);
- for (int j = 0; j < targets.size(); j++) {
- addAncestorStatesToEnter(targets[j], ancestor, statesToEnter, statesForDefaultEntry);
- }
- }
-}
-void Interpreter::addDescendentStatesToEnter(const Arabica::DOM::Node<std::string>& state,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) {
- if (isHistory(state)) {
- if (_historyValue.find(ATTR(state, "id")) != _historyValue.end()) {
- Arabica::XPath::NodeSet<std::string> history = _historyValue[ATTR(state, "id")];
- for (int i = 0; i < history.size(); i++) {
- addDescendentStatesToEnter(history[i], statesToEnter, statesForDefaultEntry);
- addAncestorStatesToEnter(history[i], state.getParentNode(), statesToEnter, statesForDefaultEntry);
- }
- } else {
- NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", state);
- for (int i = 0; i < transitions.size(); i++) {
- NodeSet<std::string> targets = getTargetStates(transitions[i]);
- for (int j = 0; j < targets.size(); j++) {
- addDescendentStatesToEnter(targets[j],statesToEnter,statesForDefaultEntry);
- addAncestorStatesToEnter(targets[j], state.getParentNode(), statesToEnter, statesForDefaultEntry);
- }
- }
- }
- } else {
- statesToEnter.push_back(state);
- if (isCompound(state)) {
- statesForDefaultEntry.push_back(state);
- Node<std::string> initial = getInitialState(state);
- addDescendentStatesToEnter(initial, statesToEnter, statesForDefaultEntry);
- addAncestorStatesToEnter(initial, state.getParentNode(), statesToEnter, statesForDefaultEntry);
- } else if (isParallel(state)) {
- NodeSet<std::string> childs = getChildStates(state);
- for (int i = 0; i < childs.size(); i++) {
- bool someAreDescendants = false;
- for (int j = 0; i < statesToEnter.size(); j++) {
- if (isDescendant(statesToEnter[j], childs[i]))
- someAreDescendants = true;
- }
- if (!someAreDescendants) {
- addDescendentStatesToEnter(childs[i], statesToEnter, statesForDefaultEntry);
- }
- }
- }
- }
-}
-
-void Interpreter::addAncestorStatesToEnter(const Arabica::DOM::Node<std::string>& state,
- const Arabica::DOM::Node<std::string>& ancestor,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) {
- NodeSet<std::string> properAncs = getProperAncestors(state, ancestor);
- for (int k = 0; k < properAncs.size(); k++) {
- statesToEnter.push_back(properAncs[k]);
- if (isParallel(properAncs[k])) {
- NodeSet<std::string> childs = getChildStates(properAncs[k]);
- for (int i = 0; i < childs.size(); i++) {
- bool someAreDescendants = false;
- for (int j = 0; i < statesToEnter.size(); j++) {
- if (isDescendant(statesToEnter[j], childs[i]))
- someAreDescendants = true;
- }
- if (!someAreDescendants) {
- addDescendentStatesToEnter(childs[i], statesToEnter, statesForDefaultEntry);
- }
- }
- }
- }
-}
-
-Arabica::DOM::Node<std::string> Interpreter::getTransitionDomain(const Arabica::DOM::Node<std::string>& transition) {
- Arabica::DOM::Node<std::string> source = getSourceState(transition);
- if (isTargetless(transition)) {
- return source;
- }
-
- Arabica::XPath::NodeSet<std::string> targets = getTargetStates(transition);
- if (boost::iequals(ATTR(transition, "type"), "internal") && isCompound(source)) {
- bool allDescendants = true;
- for (int i = 0; i < targets.size(); i++) {
- if (!isDescendant(targets[i], source)) {
- allDescendants = false;
- break;
- }
- }
- if (allDescendants)
- return source;
- }
-
- targets.push_back(source);
- return findLCCA(targets);
-}
-
-#endif
-
bool Interpreter::parentIsScxmlState(Arabica::DOM::Node<std::string> state) {
Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)state;
Arabica::DOM::Element<std::string> parentElem = (Arabica::DOM::Element<std::string>)state.getParentNode();
@@ -2253,6 +1150,16 @@ NEXT_ANCESTOR:
return ancestor;
}
+Arabica::XPath::NodeSet<std::string> Interpreter::getStates(const std::vector<std::string>& stateIds) {
+ Arabica::XPath::NodeSet<std::string> states;
+ std::vector<std::string>::const_iterator tokenIter = stateIds.begin();
+ while(tokenIter != stateIds.end()) {
+ states.push_back(getState(*tokenIter));
+ tokenIter++;
+ }
+ return states;
+}
+
Arabica::DOM::Node<std::string> Interpreter::getState(const std::string& stateId) {
if (_cachedStates.find(stateId) != _cachedStates.end()) {
@@ -2304,7 +1211,7 @@ Arabica::DOM::Node<std::string> Interpreter::getSourceState(const Arabica::DOM::
* attribute nor an <initial> element is specified, the SCXML Processor must use
* the first child state in document order as the default initial state.
*/
-Arabica::DOM::Node<std::string> Interpreter::getInitialState(Arabica::DOM::Node<std::string> state) {
+Arabica::XPath::NodeSet<std::string> Interpreter::getInitialStates(Arabica::DOM::Node<std::string> state) {
if (!state) {
state = _document.getFirstChild();
while(state && !isState(state))
@@ -2317,25 +1224,34 @@ Arabica::DOM::Node<std::string> Interpreter::getInitialState(Arabica::DOM::Node<
assert(isCompound(state) || isParallel(state));
+ Arabica::XPath::NodeSet<std::string> initialStates;
+
// initial attribute at element
Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)state;
if (stateElem.hasAttribute("initial")) {
- return getState(stateElem.getAttribute("initial"));
+ return getStates(tokenizeIdRefs(stateElem.getAttribute("initial")));
}
+ Arabica::XPath::NodeSet<std::string> initStates;
+
// initial element as child - but not the implicit generated one
- NodeSet<std::string> initialStates = filterChildElements(_xmlNSPrefix + "initial", state);
- if(initialStates.size() == 1 && !boost::iequals(ATTR(initialStates[0], "generated"), "true"))
- return initialStates[0];
+ NodeSet<std::string> initElems = filterChildElements(_xmlNSPrefix + "initial", state);
+ if(initElems.size() == 1 && !boost::iequals(ATTR(initElems[0], "generated"), "true")) {
+ initStates.push_back(initialStates[0]);
+ return initStates;
+ }
// first child state
NodeList<std::string> childs = state.getChildNodes();
for (int i = 0; i < childs.getLength(); i++) {
- if (isState(childs.item(i)))
- return childs.item(i);
+ if (isState(childs.item(i))) {
+ initStates.push_back(childs.item(i));
+ return initStates;
+ }
+
}
// nothing found
- return Arabica::DOM::Node<std::string>();
+ return Arabica::XPath::NodeSet<std::string>();
}
NodeSet<std::string> Interpreter::getTargetStates(const Arabica::DOM::Node<std::string>& transition) {
@@ -2488,7 +1404,7 @@ bool Interpreter::isInitial(const Arabica::DOM::Node<std::string>& state) {
if (!isState(parent))
return true; // scxml element
- if (getInitialState(parent) == state)
+ if (isMember(state, getInitialStates(parent)))
return true; // every nested node
return false;
diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h
index 5d29eb6..353233d 100644
--- a/src/uscxml/Interpreter.h
+++ b/src/uscxml/Interpreter.h
@@ -26,8 +26,6 @@
#include "uscxml/server/InterpreterServlet.h"
-#define ORIG_ENTERSTATES
-
namespace uscxml {
class HTTPServletInvoker;
@@ -71,7 +69,16 @@ public:
std::string unit;
};
-class Interpreter : protected Arabica::SAX2DOM::Parser<std::string> {
+class SCXMLParser : public Arabica::SAX2DOM::Parser<std::string> {
+public:
+ SCXMLParser(Interpreter* interpreter);
+ void startPrefixMapping(const std::string& /* prefix */, const std::string& /* uri */);
+
+ Arabica::SAX::CatchErrorHandler<std::string> _errorHandler;
+ Interpreter* _interpreter;
+};
+
+class Interpreter {
public:
enum Binding {
EARLY = 0,
@@ -91,8 +98,6 @@ public:
static Interpreter* fromURI(const std::string& uri);
static Interpreter* fromInputSource(Arabica::SAX::InputSource<std::string>& source);
- virtual void startPrefixMapping(const std::string& /* prefix */, const std::string& /* uri */);
-
void start();
static void run(void*);
void join() {
@@ -102,7 +107,7 @@ public:
return _running || !_done;
}
- void interpret();
+ virtual void interpret() = 0;
void addMonitor(InterpreterMonitor* monitor) {
_monitors.insert(monitor);
@@ -168,7 +173,13 @@ public:
Arabica::XPath::NodeSet<std::string> getConfiguration() {
return _configuration;
}
+ void setConfiguration(const std::vector<std::string>& states) {
+ _userDefinedStartConfiguration = states;
+ }
+
Arabica::DOM::Node<std::string> getState(const std::string& stateId);
+ Arabica::XPath::NodeSet<std::string> getStates(const std::vector<std::string>& stateIds);
+
Arabica::DOM::Document<std::string>& getDocument() {
return _document;
}
@@ -203,13 +214,18 @@ public:
static bool isCompound(const Arabica::DOM::Node<std::string>& state);
static bool isDescendant(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2);
+ static std::vector<std::string> tokenizeIdRefs(const std::string& idRefs);
+
bool isInitial(const Arabica::DOM::Node<std::string>& state);
- Arabica::DOM::Node<std::string> getInitialState(Arabica::DOM::Node<std::string> state = Arabica::DOM::Node<std::string>());
+ Arabica::XPath::NodeSet<std::string> getInitialStates(Arabica::DOM::Node<std::string> state = Arabica::DOM::Node<std::string>());
static Arabica::XPath::NodeSet<std::string> getChildStates(const Arabica::DOM::Node<std::string>& state);
Arabica::XPath::NodeSet<std::string> getTargetStates(const Arabica::DOM::Node<std::string>& transition);
+ Arabica::DOM::Node<std::string> getSourceState(const Arabica::DOM::Node<std::string>& transition);
static Arabica::XPath::NodeSet<std::string> filterChildElements(const std::string& tagname, const Arabica::DOM::Node<std::string>& node);
static Arabica::XPath::NodeSet<std::string> filterChildElements(const std::string& tagName, const Arabica::XPath::NodeSet<std::string>& nodeSet);
+ Arabica::DOM::Node<std::string> findLCCA(const Arabica::XPath::NodeSet<std::string>& states);
+ Arabica::XPath::NodeSet<std::string> getProperAncestors(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2);
static const std::string getUUID();
protected:
@@ -219,8 +235,6 @@ protected:
void normalize(const Arabica::DOM::Document<std::string>& node);
void setupIOProcessors();
- void mainEventLoop();
-
bool _stable;
tthread::thread* _thread;
tthread::mutex _mutex;
@@ -241,7 +255,8 @@ protected:
Binding _binding;
Arabica::XPath::NodeSet<std::string> _configuration;
Arabica::XPath::NodeSet<std::string> _statesToInvoke;
-
+ std::vector<std::string> _userDefinedStartConfiguration;
+
DataModel _dataModel;
std::map<std::string, Arabica::XPath::NodeSet<std::string> > _historyValue;
@@ -251,52 +266,14 @@ protected:
DelayedEventQueue* _sendQueue;
Event _currEvent;
-
InterpreterServlet* _httpServlet;
-
std::set<InterpreterMonitor*> _monitors;
static URL toBaseURI(const URL& url);
- void microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
- void executeTransitionContent(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
void executeContent(const Arabica::DOM::Node<std::string>& content);
void executeContent(const Arabica::DOM::NodeList<std::string>& content);
void executeContent(const Arabica::XPath::NodeSet<std::string>& content);
- void initializeData(const Arabica::DOM::Node<std::string>& data);
- void exitInterpreter();
-
-#ifdef ORIG_ENTERSTATES
- void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
- void addStatesToEnter(const Arabica::DOM::Node<std::string>& state,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry);
-#endif
-
-#ifdef ENTERSTATES_02_2013
- void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
- void computeEntrySet(const Arabica::XPath::NodeSet<std::string>& transitions,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry);
- void addDescendentStatesToEnter(const Arabica::DOM::Node<std::string>& state,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry);
- void addAncestorStatesToEnter(const Arabica::DOM::Node<std::string>& state,
- const Arabica::DOM::Node<std::string>& ancestor,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry);
- Arabica::DOM::Node<std::string> getTransitionDomain(const Arabica::DOM::Node<std::string>& transition);
-
-#endif
-
- void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
-
- Arabica::XPath::NodeSet<std::string> selectEventlessTransitions();
- Arabica::XPath::NodeSet<std::string> selectTransitions(const std::string& event);
- Arabica::DOM::Node<std::string> getSourceState(const Arabica::DOM::Node<std::string>& transition);
- Arabica::DOM::Node<std::string> findLCCA(const Arabica::XPath::NodeSet<std::string>& states);
- Arabica::XPath::NodeSet<std::string> getProperAncestors(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2);
-
void send(const Arabica::DOM::Node<std::string>& element);
void invoke(const Arabica::DOM::Node<std::string>& element);
@@ -306,16 +283,11 @@ protected:
static void delayedSend(void* userdata, std::string eventName);
static bool nameMatch(const std::string& transitionEvent, const std::string& event);
- Arabica::XPath::NodeSet<std::string> filterPreempted(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
- bool isPreemptingTransition(const Arabica::DOM::Node<std::string>& t1, const Arabica::DOM::Node<std::string>& t2);
bool isWithinSameChild(const Arabica::DOM::Node<std::string>& transition);
bool hasConditionMatch(const Arabica::DOM::Node<std::string>& conditional);
bool isInFinalState(const Arabica::DOM::Node<std::string>& state);
bool parentIsScxmlState(Arabica::DOM::Node<std::string> state);
-// Arabica::DOM::Node<std::string> getTransitionSubgraph(const Arabica::DOM::Node<std::string>& transition);
-
- static std::vector<std::string> tokenizeIdRefs(const std::string& idRefs);
static boost::uuids::random_generator uuidGen;
@@ -327,7 +299,6 @@ protected:
Data _cmdLineOptions;
IOProcessor getIOProcessor(const std::string& type);
-// IOProcessor* getIOProcessorForId(const std::string& sendId);
std::map<std::string, IOProcessor> _ioProcessors;
std::map<std::string, std::pair<Interpreter*, SendRequest> > _sendIds;
@@ -335,9 +306,11 @@ protected:
std::map<std::string, Invoker> _autoForwardees;
std::map<Arabica::DOM::Node<std::string>, ExecutableContent> _executableContent;
- /// We need to remember to adapt them when the DOM is operated upon
+ /// TODO: We need to remember to adapt them when the DOM is operated upon
std::map<std::string, Arabica::DOM::Node<std::string> > _cachedStates;
std::map<std::string, URL> _cachedURLs;
+
+ friend class SCXMLParser;
};
}
diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp
index 13b0b55..845118b 100644
--- a/src/uscxml/Message.cpp
+++ b/src/uscxml/Message.cpp
@@ -179,6 +179,10 @@ Data Data::fromXML(const std::string& xmlString) {
Data Data::fromJSON(const std::string& jsonString) {
Data data;
+
+ if (jsonString.length() == 0)
+ return data;
+
jsmn_parser p;
jsmntok_t* t = NULL;
diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp
new file mode 100644
index 0000000..d95ae4c
--- /dev/null
+++ b/src/uscxml/interpreter/InterpreterDraft6.cpp
@@ -0,0 +1,920 @@
+#include "InterpreterDraft6.h"
+
+#include <glog/logging.h>
+
+namespace uscxml {
+
+using namespace Arabica::XPath;
+using namespace Arabica::DOM;
+
+// see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation
+void InterpreterDraft6::interpret() {
+ if (!_isInitialized)
+ init();
+
+ if (!_scxml)
+ return;
+// dump();
+
+ _sessionId = getUUID();
+
+ std::string datamodelName;
+ if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel"))
+ datamodelName = ATTR(_scxml, "datamodel");
+ if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "profile")) // SCION SCXML uses profile to specify datamodel
+ datamodelName = ATTR(_scxml, "profile");
+ if(datamodelName.length() > 0)
+ _dataModel = Factory::createDataModel(datamodelName, this);
+ if(datamodelName.length() > 0 && !_dataModel) {
+ LOG(ERROR) << "No datamodel for " << datamodelName << " registered";
+ }
+
+ if (_dataModel) {
+ _dataModel.assign("_x.args", _cmdLineOptions);
+ if (_httpServlet) {
+ Data data;
+ data.compound["location"] = Data(_httpServlet->getURL(), Data::VERBATIM);
+ _dataModel.assign("_ioprocessors['http']", data);
+ }
+ }
+
+ setupIOProcessors();
+
+ _running = true;
+ _binding = (HAS_ATTR(_scxml, "binding") && boost::iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY);
+
+ // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding
+
+ if (_dataModel && _binding == EARLY) {
+ // initialize all data elements
+ NodeSet<std::string> dataElems = _xpath.evaluate("//" + _xpathPrefix + "data", _document).asNodeSet();
+ for (unsigned int i = 0; i < dataElems.size(); i++) {
+ initializeData(dataElems[i]);
+ }
+ } else if(_dataModel) {
+ // initialize current data elements
+ NodeSet<std::string> topDataElems = filterChildElements(_xmlNSPrefix + "data", filterChildElements(_xmlNSPrefix + "datamodel", _scxml));
+ for (unsigned int i = 0; i < topDataElems.size(); i++) {
+ initializeData(topDataElems[i]);
+ }
+ }
+
+ // executeGlobalScriptElements
+ NodeSet<std::string> globalScriptElems = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "script", _document).asNodeSet();
+ for (unsigned int i = 0; i < globalScriptElems.size(); i++) {
+ if (_dataModel)
+ executeContent(globalScriptElems[i]);
+ }
+
+ NodeSet<std::string> initialTransitions;
+
+ if (_userDefinedStartConfiguration.size() == 0) {
+ // try to get initial transition form initial element
+ initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _document).asNodeSet();
+ }
+
+ if (initialTransitions.size() == 0) {
+ Arabica::XPath::NodeSet<std::string> initialStates;
+ if (_userDefinedStartConfiguration.size() > 0) {
+ // otherwise use user supplied config
+ initialTransitions = getStates(_userDefinedStartConfiguration);
+ } else {
+ // or fetch per draft
+ initialStates = getInitialStates();
+ }
+
+ assert(initialStates.size() > 0);
+ for (int i = 0; i < initialStates.size(); i++) {
+ Arabica::DOM::Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial");
+ initialElem.setAttribute("generated", "true");
+ Arabica::DOM::Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition");
+ transitionElem.setAttribute("target", ATTR(initialStates[i], "id"));
+ initialElem.appendChild(transitionElem);
+ _scxml.appendChild(initialElem);
+ initialTransitions.push_back(transitionElem);
+ }
+ }
+
+ assert(initialTransitions.size() > 0);
+ enterStates(initialTransitions);
+
+ assert(hasLegalConfiguration());
+ mainEventLoop();
+
+ if (_parentQueue) {
+ // send one final event to unblock eventual listeners
+ Event quit;
+ quit.name = "done.state.scxml";
+ _parentQueue->push(quit);
+ }
+
+ // set datamodel to null from this thread
+ if(_dataModel)
+ _dataModel = DataModel();
+
+}
+
+/**
+ * Called with a single data element from the topmost datamodel element.
+ */
+void InterpreterDraft6::initializeData(const Arabica::DOM::Node<std::string>& data) {
+ if (!_dataModel) {
+ LOG(ERROR) << "Cannot initialize data when no datamodel is given!";
+ return;
+ }
+ try {
+ if (!HAS_ATTR(data, "id")) {
+ LOG(ERROR) << "Data element has no id!";
+ return;
+ }
+
+ if (HAS_ATTR(data, "expr")) {
+ std::string value = ATTR(data, "expr");
+ _dataModel.assign(ATTR(data, "id"), value);
+ } else if (HAS_ATTR(data, "src")) {
+ URL srcURL(ATTR(data, "src"));
+ if (!srcURL.isAbsolute())
+ toAbsoluteURI(srcURL);
+
+ std::stringstream ss;
+ if (_cachedURLs.find(srcURL.asString()) != _cachedURLs.end()) {
+ ss << _cachedURLs[srcURL.asString()];
+ } else {
+ ss << srcURL;
+ _cachedURLs[srcURL.asString()] = srcURL;
+ }
+ _dataModel.assign(ATTR(data, "id"), ss.str());
+
+ } else if (data.hasChildNodes()) {
+ // search for the text node with the actual script
+ NodeList<std::string> dataChilds = data.getChildNodes();
+ for (int i = 0; i < dataChilds.getLength(); i++) {
+ if (dataChilds.item(i).getNodeType() == Node_base::TEXT_NODE) {
+ Data value = Data(dataChilds.item(i).getNodeValue());
+ _dataModel.assign(ATTR(data, "id"), value);
+ break;
+ }
+ }
+ }
+
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error in data element:" << std::endl << e << std::endl;
+ }
+}
+
+void InterpreterDraft6::mainEventLoop() {
+ std::set<InterpreterMonitor*>::iterator monIter;
+
+ while(_running) {
+ NodeSet<std::string> enabledTransitions;
+ _stable = false;
+
+ // Here we handle eventless transitions and transitions
+ // triggered by internal events until machine is stable
+ while(_running && !_stable) {
+#if 0
+ std::cout << "Configuration: ";
+ for (int i = 0; i < _configuration.size(); i++) {
+ std::cout << ATTR(_configuration[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+#endif
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ try {
+ (*monIter)->beforeMicroStep(this);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling beforeMicroStep on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling beforeMicroStep on monitors";
+ }
+ monIter++;
+ }
+
+ enabledTransitions = selectEventlessTransitions();
+ if (enabledTransitions.size() == 0) {
+ if (_internalQueue.size() == 0) {
+ _stable = true;
+ } else {
+ _currEvent = _internalQueue.front();
+ _internalQueue.pop_front();
+#if VERBOSE
+ std::cout << "Received internal event " << _currEvent.name << std::endl;
+#endif
+ if (_dataModel)
+ _dataModel.setEvent(_currEvent);
+ enabledTransitions = selectTransitions(_currEvent.name);
+ }
+ }
+ if (!enabledTransitions.empty()) {
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ try {
+ (*monIter)->beforeTakingTransitions(this, enabledTransitions);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling beforeTakingTransitions on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling beforeTakingTransitions on monitors";
+ }
+ monIter++;
+ }
+ microstep(enabledTransitions);
+ }
+ }
+
+ for (unsigned int i = 0; i < _statesToInvoke.size(); i++) {
+ NodeSet<std::string> invokes = filterChildElements(_xmlNSPrefix + "invoke", _statesToInvoke[i]);
+ for (unsigned int j = 0; j < invokes.size(); j++) {
+ invoke(invokes[j]);
+ }
+ }
+
+ _statesToInvoke = NodeSet<std::string>();
+ if (!_internalQueue.empty())
+ continue;
+
+ // assume that we have a legal configuration as soon as the internal queue is empty
+ assert(hasLegalConfiguration());
+
+ monIter = _monitors.begin();
+// if (!_sendQueue || _sendQueue->isEmpty()) {
+ while(monIter != _monitors.end()) {
+ try {
+ (*monIter)->onStableConfiguration(this);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling onStableConfiguration on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling onStableConfiguration on monitors";
+ }
+ monIter++;
+ }
+// }
+
+ // whenever we have a stable configuration, run the mainThread hooks with 200fps
+ while(_externalQueue.isEmpty() && _thread == NULL) {
+ runOnMainThread(200);
+ }
+
+ _currEvent = _externalQueue.pop();
+#if VERBOSE
+ std::cout << "Received externalEvent event " << _currEvent.name << std::endl;
+#endif
+ _currEvent.type = Event::EXTERNAL; // make sure it is set to external
+ if (!_running)
+ exitInterpreter();
+
+ if (_dataModel && boost::iequals(_currEvent.name, "cancel.invoke." + _sessionId))
+ break;
+
+ if (_dataModel)
+ try {
+ _dataModel.setEvent(_currEvent);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl;
+ }
+ for (unsigned int i = 0; i < _configuration.size(); i++) {
+ NodeSet<std::string> invokes = filterChildElements(_xmlNSPrefix + "invoke", _configuration[i]);
+ for (unsigned int j = 0; j < invokes.size(); j++) {
+ Arabica::DOM::Element<std::string> invokeElem = (Arabica::DOM::Element<std::string>)invokes[j];
+ std::string invokeId;
+ if (HAS_ATTR(invokeElem, "id"))
+ invokeId = ATTR(invokeElem, "id");
+ if (HAS_ATTR(invokeElem, "idlocation") && _dataModel)
+ invokeId = _dataModel.evalAsString(ATTR(invokeElem, "idlocation"));
+
+ std::string autoForward = invokeElem.getAttribute("autoforward");
+ if (boost::iequals(invokeId, _currEvent.invokeid)) {
+
+ Arabica::XPath::NodeSet<std::string> finalizes = filterChildElements(_xmlNSPrefix + "finalize", invokeElem);
+ for (int k = 0; k < finalizes.size(); k++) {
+ Arabica::DOM::Element<std::string> finalizeElem = Arabica::DOM::Element<std::string>(finalizes[k]);
+ executeContent(finalizeElem);
+ }
+
+ }
+ if (boost::iequals(autoForward, "true")) {
+ try {
+ _invokers[invokeId].send(_currEvent);
+ } catch(...) {
+ LOG(ERROR) << "Exception caught while sending event to invoker " << invokeId;
+ }
+ }
+ }
+ }
+ enabledTransitions = selectTransitions(_currEvent.name);
+ if (!enabledTransitions.empty())
+ microstep(enabledTransitions);
+ }
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ try {
+ (*monIter)->beforeCompletion(this);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling beforeCompletion on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling beforeCompletion on monitors";
+ }
+ monIter++;
+ }
+
+ exitInterpreter();
+
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ try {
+ (*monIter)->afterCompletion(this);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling afterCompletion on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling afterCompletion on monitors";
+ }
+ monIter++;
+ }
+
+}
+
+Arabica::XPath::NodeSet<std::string> InterpreterDraft6::selectTransitions(const std::string& event) {
+ Arabica::XPath::NodeSet<std::string> enabledTransitions;
+
+ NodeSet<std::string> atomicStates;
+ for (unsigned int i = 0; i < _configuration.size(); i++) {
+ if (isAtomic(_configuration[i]))
+ atomicStates.push_back(_configuration[i]);
+ }
+ atomicStates.to_document_order();
+
+ for (unsigned int i = 0; i < atomicStates.size(); i++) {
+ NodeSet<std::string> ancestors = getProperAncestors(atomicStates[i], Arabica::DOM::Node<std::string>());
+
+ NodeSet<std::string> sortedAncestors;
+ sortedAncestors.push_back(atomicStates[i]);
+ sortedAncestors.insert(sortedAncestors.end(), ancestors.begin(), ancestors.end());
+
+ for (unsigned int j = 0; j < sortedAncestors.size(); j++) {
+ NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", sortedAncestors[j]);
+ for (unsigned int k = 0; k < transitions.size(); k++) {
+ std::string eventName;
+ if (HAS_ATTR(transitions[k], "event")) {
+ eventName = ATTR(transitions[k], "event");
+ } else if(HAS_ATTR(transitions[k], "eventexpr")) {
+ if (_dataModel) {
+ eventName = _dataModel.evalAsString(ATTR(transitions[k], "eventexpr"));
+ } else {
+ LOG(ERROR) << "Transition has eventexpr attribute with no datamodel defined";
+ goto LOOP;
+ }
+ } else {
+ goto LOOP;
+ }
+
+ if (eventName.length() > 0 &&
+ nameMatch(eventName, event) &&
+ hasConditionMatch(transitions[k])) {
+ enabledTransitions.push_back(transitions[k]);
+ goto LOOP;
+ }
+ }
+ }
+LOOP:
+ ;
+ }
+
+ enabledTransitions = filterPreempted(enabledTransitions);
+ return enabledTransitions;
+}
+
+Arabica::XPath::NodeSet<std::string> InterpreterDraft6::selectEventlessTransitions() {
+ Arabica::XPath::NodeSet<std::string> enabledTransitions;
+
+ NodeSet<std::string> atomicStates;
+ for (unsigned int i = 0; i < _configuration.size(); i++) {
+ if (isAtomic(_configuration[i]))
+ atomicStates.push_back(_configuration[i]);
+ }
+ atomicStates.to_document_order();
+
+ for (unsigned int i = 0; i < atomicStates.size(); i++) {
+ NodeSet<std::string> ancestors = getProperAncestors(atomicStates[i], Arabica::DOM::Node<std::string>());
+ ancestors.push_back(atomicStates[i]);
+ for (unsigned int j = 0; j < ancestors.size(); j++) {
+ NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", ancestors[j]);
+ for (unsigned int k = 0; k < transitions.size(); k++) {
+ if (!HAS_ATTR(transitions[k], "event") && hasConditionMatch(transitions[k])) {
+ enabledTransitions.push_back(transitions[k]);
+ goto LOOP;
+ }
+ }
+
+#if 0
+ NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", ancestors[j]);
+ for (unsigned int k = 0; k < transitions.size(); k++) {
+ if (!((Arabica::DOM::Element<std::string>)transitions[k]).hasAttribute("event") && hasConditionMatch(transitions[k])) {
+ enabledTransitions.push_back(transitions[k]);
+ goto LOOP;
+ }
+ }
+#endif
+ }
+LOOP:
+ ;
+ }
+
+ enabledTransitions = filterPreempted(enabledTransitions);
+ return enabledTransitions;
+}
+
+Arabica::XPath::NodeSet<std::string> InterpreterDraft6::filterPreempted(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+ Arabica::XPath::NodeSet<std::string> filteredTransitions;
+ for (unsigned int i = 0; i < enabledTransitions.size(); i++) {
+ Arabica::DOM::Node<std::string> t = enabledTransitions[i];
+ for (unsigned int j = i+1; j < enabledTransitions.size(); j++) {
+ Arabica::DOM::Node<std::string> t2 = enabledTransitions[j];
+ if (isPreemptingTransition(t2, t)) {
+#if VERBOSE
+ std::cout << "Transition preempted!: " << std::endl << t2 << std::endl << t << std::endl;
+#endif
+ goto LOOP;
+ }
+ }
+ filteredTransitions.push_back(t);
+LOOP:
+ ;
+ }
+ return filteredTransitions;
+}
+
+bool InterpreterDraft6::isPreemptingTransition(const Arabica::DOM::Node<std::string>& t1, const Arabica::DOM::Node<std::string>& t2) {
+ assert(t1);
+ assert(t2);
+
+#if VERBOSE
+ std::cout << "Checking preemption: " << std::endl << t1 << std::endl << t2 << std::endl;
+#endif
+
+#if 1
+ if (t1 == t2)
+ return false;
+ if (isWithinSameChild(t1) && (!isTargetless(t2) && !isWithinSameChild(t2)))
+ return true;
+ if (!isTargetless(t1) && !isWithinSameChild(t1))
+ return true;
+ return false;
+#endif
+
+}
+
+void InterpreterDraft6::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+#if 0
+ std::cout << "Transitions: ";
+ for (int i = 0; i < enabledTransitions.size(); i++) {
+ std::cout << ((Arabica::DOM::Element<std::string>)getSourceState(enabledTransitions[i])).getAttribute("id") << " -> " << std::endl;
+ NodeSet<std::string> targetSet = getTargetStates(enabledTransitions[i]);
+ for (int j = 0; j < targetSet.size(); j++) {
+ std::cout << " " << ((Arabica::DOM::Element<std::string>)targetSet[j]).getAttribute("id") << std::endl;
+ }
+ }
+ std::cout << std::endl;
+#endif
+
+ exitStates(enabledTransitions);
+ executeContent(enabledTransitions);
+ enterStates(enabledTransitions);
+}
+
+void InterpreterDraft6::exitInterpreter() {
+ NodeSet<std::string> statesToExit = _configuration;
+ statesToExit.to_document_order();
+ statesToExit.reverse();
+
+ for (int i = 0; i < statesToExit.size(); i++) {
+ Arabica::XPath::NodeSet<std::string> onExitElems = filterChildElements(_xmlNSPrefix + "onexit", statesToExit[i]);
+ for (int j = 0; j < onExitElems.size(); j++) {
+ executeContent(onExitElems[j]);
+ }
+ Arabica::XPath::NodeSet<std::string> invokeElems = filterChildElements(_xmlNSPrefix + "invoke", statesToExit[i]);
+ for (int j = 0; j < invokeElems.size(); j++) {
+ cancelInvoke(invokeElems[j]);
+ }
+ if (isFinal(statesToExit[i]) && parentIsScxmlState(statesToExit[i])) {
+ returnDoneEvent(statesToExit[i]);
+ }
+ }
+ _configuration = NodeSet<std::string>();
+}
+
+
+void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+ NodeSet<std::string> statesToExit;
+ std::set<InterpreterMonitor*>::iterator monIter;
+
+#if VERBOSE
+ std::cout << "Enabled exit transitions: " << std::endl;
+ for (int i = 0; i < enabledTransitions.size(); i++) {
+ std::cout << enabledTransitions[i] << std::endl;
+ }
+ std::cout << std::endl;
+#endif
+
+ for (int i = 0; i < enabledTransitions.size(); i++) {
+ Arabica::DOM::Element<std::string> transition = ((Arabica::DOM::Element<std::string>)enabledTransitions[i]);
+ if (!isTargetless(transition)) {
+ std::string transitionType = (boost::iequals(transition.getAttribute("type"), "internal") ? "internal" : "external");
+ NodeSet<std::string> tStates = getTargetStates(transition);
+ Arabica::DOM::Node<std::string> ancestor;
+ Arabica::DOM::Node<std::string> source = getSourceState(transition);
+
+ bool allDescendants = true;
+ for (int j = 0; j < tStates.size(); j++) {
+ if (!isDescendant(tStates[j], source)) {
+ allDescendants = false;
+ break;
+ }
+ }
+ if (boost::iequals(transitionType, "internal") &&
+ isCompound(source) &&
+ allDescendants) {
+ ancestor = source;
+ } else {
+ NodeSet<std::string> tmpStates;
+ tmpStates.push_back(source);
+ tmpStates.insert(tmpStates.end(), tStates.begin(), tStates.end());
+
+#if VERBOSE
+ std::cout << "tmpStates: ";
+ for (int i = 0; i < tmpStates.size(); i++) {
+ std::cout << ATTR(tmpStates[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+#endif
+ ancestor = findLCCA(tmpStates);
+ }
+
+#if VERBOSE
+ std::cout << "Ancestor: " << ATTR(ancestor, "id") << std::endl;;
+#endif
+
+ for (int j = 0; j < _configuration.size(); j++) {
+ if (isDescendant(_configuration[j], ancestor))
+ statesToExit.push_back(_configuration[j]);
+ }
+ }
+ }
+
+#if VERBOSE
+ std::cout << "States to exit: ";
+ for (int i = 0; i < statesToExit.size(); i++) {
+ std::cout << LOCALNAME(statesToExit[i]) << ":" << ATTR(statesToExit[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+
+#endif
+
+ // remove statesToExit from _statesToInvoke
+ std::list<Arabica::DOM::Node<std::string> > tmp;
+ for (int 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.to_document_order();
+ statesToExit.reverse();
+
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ try {
+ (*monIter)->beforeExitingStates(this, statesToExit);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling beforeExitingStates on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling beforeExitingStates on monitors";
+ }
+ monIter++;
+ }
+
+ for (int i = 0; i < statesToExit.size(); i++) {
+ NodeSet<std::string> histories = filterChildElements(_xmlNSPrefix + "history", statesToExit[i]);
+ for (int j = 0; j < histories.size(); j++) {
+ Arabica::DOM::Element<std::string> historyElem = (Arabica::DOM::Element<std::string>)histories[j];
+ std::string historyType = (historyElem.hasAttribute("type") ? historyElem.getAttribute("type") : "shallow");
+ NodeSet<std::string> historyNodes;
+ for (int k = 0; k < _configuration.size(); k++) {
+ if (boost::iequals(historyType, "deep")) {
+ if (isAtomic(_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 0
+ std::cout << "History node " << ATTR(historyElem, "id") << " contains: ";
+ for (int i = 0; i < historyNodes.size(); i++) {
+ std::cout << ATTR(historyNodes[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+
+#endif
+
+ }
+ }
+
+ for (int i = 0; i < statesToExit.size(); i++) {
+ NodeSet<std::string> onExits = filterChildElements(_xmlNSPrefix + "onExit", statesToExit[i]);
+ for (int j = 0; j < onExits.size(); j++) {
+ Arabica::DOM::Element<std::string> onExitElem = (Arabica::DOM::Element<std::string>)onExits[j];
+ executeContent(onExitElem);
+ }
+ NodeSet<std::string> invokes = filterChildElements(_xmlNSPrefix + "invoke", statesToExit[i]);
+ for (int j = 0; j < invokes.size(); j++) {
+ Arabica::DOM::Element<std::string> invokeElem = (Arabica::DOM::Element<std::string>)invokes[j];
+ cancelInvoke(invokeElem);
+ }
+ }
+
+ // remove statesToExit from _configuration
+ tmp.clear();
+ for (int i = 0; i < _configuration.size(); i++) {
+ if (!isMember(_configuration[i], statesToExit)) {
+ tmp.push_back(_configuration[i]);
+ }
+ }
+ _configuration = NodeSet<std::string>();
+ _configuration.insert(_configuration.end(), tmp.begin(), tmp.end());
+
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ try {
+ (*monIter)->afterExitingStates(this);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling afterExitingStates on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling afterExitingStates on monitors";
+ }
+ monIter++;
+ }
+
+}
+
+void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+ NodeSet<std::string> statesToEnter;
+ NodeSet<std::string> statesForDefaultEntry;
+ std::set<InterpreterMonitor*>::iterator monIter;
+
+#if VERBOSE
+ std::cout << "Enabled enter transitions: " << std::endl;
+ for (int i = 0; i < enabledTransitions.size(); i++) {
+ std::cout << enabledTransitions[i] << std::endl;
+ }
+ std::cout << std::endl;
+#endif
+
+ for (int i = 0; i < enabledTransitions.size(); i++) {
+ Arabica::DOM::Element<std::string> transition = ((Arabica::DOM::Element<std::string>)enabledTransitions[i]);
+ if (!isTargetless(transition)) {
+ std::string transitionType = (boost::iequals(transition.getAttribute("type"), "internal") ? "internal" : "external");
+ NodeSet<std::string> tStates = getTargetStates(transition);
+
+#if VERBOSE
+ std::cout << "Target States: ";
+ for (int i = 0; i < tStates.size(); i++) {
+ std::cout << ATTR(tStates[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+#endif
+
+ Arabica::DOM::Node<std::string> ancestor;
+ Arabica::DOM::Node<std::string> source = getSourceState(transition);
+#if VERBOSE
+ std::cout << "Source States: " << ATTR(source, "id") << std::endl;
+#endif
+ assert(source);
+
+ bool allDescendants = true;
+ for (int j = 0; j < tStates.size(); j++) {
+ if (!isDescendant(tStates[j], source)) {
+ allDescendants = false;
+ break;
+ }
+ }
+ if (boost::iequals(transitionType, "internal") &&
+ isCompound(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 << "Ancestor: " << ATTR(ancestor, "id") << std::endl;
+#endif
+
+ for (int j = 0; j < tStates.size(); j++) {
+ addStatesToEnter(tStates[j], statesToEnter, statesForDefaultEntry);
+ }
+
+#if VERBOSE
+ std::cout << "States to enter: ";
+ for (int i = 0; i < statesToEnter.size(); i++) {
+ std::cout << LOCALNAME(statesToEnter[i]) << ":" << ATTR(statesToEnter[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+#endif
+
+ for (int j = 0; j < tStates.size(); j++) {
+ NodeSet<std::string> ancestors = getProperAncestors(tStates[j], ancestor);
+
+#if VERBOSE
+ std::cout << "Proper Ancestors of " << ATTR(tStates[j], "id") << " and " << ATTR(ancestor, "id") << ": ";
+ for (int i = 0; i < ancestors.size(); i++) {
+ std::cout << ATTR(ancestors[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+#endif
+
+ for (int k = 0; k < ancestors.size(); k++) {
+ statesToEnter.push_back(ancestors[k]);
+ if(isParallel(ancestors[k])) {
+ NodeSet<std::string> childs = getChildStates(ancestors[k]);
+ for (int l = 0; l < childs.size(); l++) {
+ bool someIsDescendant = false;
+ for (int m = 0; m < statesToEnter.size(); m++) {
+ if (isDescendant(statesToEnter[m], childs[l])) {
+ someIsDescendant = true;
+ break;
+ }
+ }
+ if (!someIsDescendant) {
+ addStatesToEnter(childs[l], statesToEnter, statesForDefaultEntry);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ statesToEnter.to_document_order();
+
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ try {
+ (*monIter)->beforeEnteringStates(this, statesToEnter);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling beforeEnteringStates on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling beforeEnteringStates on monitors";
+ }
+ monIter++;
+ }
+
+ for (int i = 0; i < statesToEnter.size(); i++) {
+ Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)statesToEnter[i];
+ _configuration.push_back(stateElem);
+ _statesToInvoke.push_back(stateElem);
+ if (_binding == LATE && stateElem.getAttribute("isFirstEntry").size() > 0) {
+ NodeSet<std::string> dataModelElems = filterChildElements(_xmlNSPrefix + "datamodel", stateElem);
+ if(dataModelElems.size() > 0 && _dataModel) {
+ Arabica::XPath::NodeSet<std::string> dataElems = filterChildElements(_xmlNSPrefix + "data", dataModelElems[0]);
+ for (int j = 0; j < dataElems.size(); j++) {
+ initializeData(dataElems[j]);
+ }
+ }
+ stateElem.setAttribute("isFirstEntry", "");
+ }
+ // execute onentry executable content
+ NodeSet<std::string> onEntryElems = filterChildElements(_xmlNSPrefix + "onEntry", stateElem);
+ executeContent(onEntryElems);
+
+ if (isMember(stateElem, statesForDefaultEntry)) {
+ // execute initial transition content for compund states
+ Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", stateElem).asNodeSet();
+ for (int j = 0; j < transitions.size(); j++) {
+ executeContent(transitions[j]);
+ }
+ }
+
+ if (isFinal(stateElem)) {
+ internalDoneSend(stateElem);
+ Arabica::DOM::Element<std::string> parent = (Arabica::DOM::Element<std::string>)stateElem.getParentNode();
+
+ if (isParallel(parent.getParentNode())) {
+ Arabica::DOM::Element<std::string> grandParent = (Arabica::DOM::Element<std::string>)parent.getParentNode();
+
+ Arabica::XPath::NodeSet<std::string> childs = getChildStates(grandParent);
+ bool inFinalState = true;
+ for (int j = 0; j < childs.size(); j++) {
+ if (!isInFinalState(childs[j])) {
+ inFinalState = false;
+ break;
+ }
+ }
+ if (inFinalState) {
+ internalDoneSend(parent);
+ }
+ }
+ }
+ }
+ for (int i = 0; i < _configuration.size(); i++) {
+ Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)_configuration[i];
+ if (isFinal(stateElem) && parentIsScxmlState(stateElem)) {
+ _running = false;
+ _done = true;
+ }
+ }
+
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ try {
+ (*monIter)->afterEnteringStates(this);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error when calling afterEnteringStates on monitors: " << std::endl << e << std::endl;
+ } catch (...) {
+ LOG(ERROR) << "An exception occured when calling afterEnteringStates on monitors";
+ }
+ monIter++;
+ }
+
+}
+
+void InterpreterDraft6::addStatesToEnter(const Arabica::DOM::Node<std::string>& state,
+ Arabica::XPath::NodeSet<std::string>& statesToEnter,
+ Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) {
+ std::string stateId = ((Arabica::DOM::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 (int i = 0; i < historyValue.size(); i++) {
+ std::cout << ATTR(historyValue[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+#endif
+
+ for (int i = 0; i < historyValue.size(); i++) {
+ addStatesToEnter(historyValue[i], statesToEnter, statesForDefaultEntry);
+ NodeSet<std::string> ancestors = getProperAncestors(historyValue[i], state);
+
+#if VERBOSE
+ std::cout << "Proper Ancestors: ";
+ for (int i = 0; i < ancestors.size(); i++) {
+ std::cout << ATTR(ancestors[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+#endif
+
+ for (int j = 0; j < ancestors.size(); j++) {
+ statesToEnter.push_back(ancestors[j]);
+ }
+ }
+ } else {
+ NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", state);
+ for (int i = 0; i < transitions.size(); i++) {
+ NodeSet<std::string> targets = getTargetStates(transitions[i]);
+ for (int j = 0; j < targets.size(); j++) {
+ addStatesToEnter(targets[j], statesToEnter, statesForDefaultEntry);
+
+ // Modifications from chris nuernberger
+ NodeSet<std::string> ancestors = getProperAncestors(targets[j], state);
+ for (int 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 (int i = 0; i < tStates.size(); i++) {
+ addStatesToEnter(tStates[i], statesToEnter, statesForDefaultEntry);
+ }
+
+ // addStatesToEnter(getInitialState(state), statesToEnter, statesForDefaultEntry);
+ // NodeSet<std::string> tStates = getTargetStates(getInitialState(state));
+
+ } else if(isParallel(state)) {
+ NodeSet<std::string> childStates = getChildStates(state);
+ for (int i = 0; i < childStates.size(); i++) {
+ addStatesToEnter(childStates[i], statesToEnter, statesForDefaultEntry);
+ }
+ }
+ }
+}
+
+
+} \ No newline at end of file
diff --git a/src/uscxml/interpreter/InterpreterDraft6.h b/src/uscxml/interpreter/InterpreterDraft6.h
new file mode 100644
index 0000000..10ff10a
--- /dev/null
+++ b/src/uscxml/interpreter/InterpreterDraft6.h
@@ -0,0 +1,31 @@
+#ifndef INTERPRETERDRAFT6_H_JAXK9FE1
+#define INTERPRETERDRAFT6_H_JAXK9FE1
+
+#include "uscxml/Interpreter.h"
+
+namespace uscxml {
+
+class InterpreterDraft6 : public Interpreter {
+ void interpret();
+ void mainEventLoop();
+
+ void initializeData(const Arabica::DOM::Node<std::string>& data);
+ void microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
+ void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
+ void addStatesToEnter(const Arabica::DOM::Node<std::string>& state,
+ Arabica::XPath::NodeSet<std::string>& statesToEnter,
+ Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry);
+
+ void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
+ void exitInterpreter();
+
+ Arabica::XPath::NodeSet<std::string> selectEventlessTransitions();
+ Arabica::XPath::NodeSet<std::string> selectTransitions(const std::string& event);
+ Arabica::XPath::NodeSet<std::string> filterPreempted(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
+ bool isPreemptingTransition(const Arabica::DOM::Node<std::string>& t1, const Arabica::DOM::Node<std::string>& t2);
+
+};
+
+}
+
+#endif /* end of include guard: INTERPRETERDRAFT6_H_JAXK9FE1 */
diff --git a/src/uscxml/interpreter/InterpreterDraft7.cpp b/src/uscxml/interpreter/InterpreterDraft7.cpp
new file mode 100644
index 0000000..9542146
--- /dev/null
+++ b/src/uscxml/interpreter/InterpreterDraft7.cpp
@@ -0,0 +1,439 @@
+#include "InterpreterDraft7.h"
+
+#include <glog/logging.h>
+
+namespace uscxml {
+
+using namespace Arabica::XPath;
+using namespace Arabica::DOM;
+
+/**
+procedure interpret(doc):
+ if not valid(doc): failWithError()
+ expandScxmlSource(doc)
+ configuration = new OrderedSet()
+ statesToInvoke = new OrderedSet()
+ internalQueue = new Queue()
+ externalQueue = new BlockingQueue()
+ historyValue = new HashTable()
+ datamodel = new Datamodel(doc)
+ if doc.binding == "early":
+ initializeDatamodel(datamodel, doc)
+ running = true
+ executeGlobalScriptElement(doc)
+ enterStates([doc.initial.transition])
+ mainEventLoop()
+ */
+void InterpreterDraft7::interpret() {
+ if (!_isInitialized)
+ init();
+
+ if (!_scxml)
+ return;
+
+ _sessionId = getUUID();
+
+ std::string datamodelName;
+ if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel"))
+ datamodelName = ATTR(_scxml, "datamodel");
+ if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "profile")) // SCION SCXML uses profile to specify datamodel
+ datamodelName = ATTR(_scxml, "profile");
+ if(datamodelName.length() > 0)
+ _dataModel = Factory::createDataModel(datamodelName, this);
+ if(datamodelName.length() > 0 && !_dataModel) {
+ LOG(ERROR) << "No datamodel for " << datamodelName << " registered";
+ }
+
+ if (_dataModel) {
+ _dataModel.assign("_x.args", _cmdLineOptions);
+ if (_httpServlet) {
+ Data data;
+ data.compound["location"] = Data(_httpServlet->getURL(), Data::VERBATIM);
+ _dataModel.assign("_ioprocessors['http']", data);
+ }
+ }
+
+ setupIOProcessors();
+
+ _running = true;
+ _binding = (HAS_ATTR(_scxml, "binding") && boost::iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY);
+
+ // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding
+
+ if (_dataModel && _binding == EARLY) {
+ // initialize all data elements
+ NodeSet<std::string> dataElems = _xpath.evaluate("//" + _xpathPrefix + "data", _document).asNodeSet();
+ for (unsigned int i = 0; i < dataElems.size(); i++) {
+ initializeData(dataElems[i]);
+ }
+ } else if(_dataModel) {
+ // initialize current data elements
+ NodeSet<std::string> topDataElems = filterChildElements(_xmlNSPrefix + "data", filterChildElements(_xmlNSPrefix + "datamodel", _scxml));
+ for (unsigned int i = 0; i < topDataElems.size(); i++) {
+ initializeData(topDataElems[i]);
+ }
+ }
+
+ // executeGlobalScriptElements
+ NodeSet<std::string> globalScriptElems = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "script", _document).asNodeSet();
+ for (unsigned int i = 0; i < globalScriptElems.size(); i++) {
+ if (_dataModel)
+ executeContent(globalScriptElems[i]);
+ }
+
+ // initial transition might be implict
+ NodeSet<std::string> initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _document).asNodeSet();
+ if (initialTransitions.size() == 0) {
+ Arabica::XPath::NodeSet<std::string> initialStates = getInitialStates();
+
+ for (int i = 0; i < initialStates.size(); i++) {
+ Arabica::DOM::Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial");
+ initialElem.setAttribute("generated", "true");
+ Arabica::DOM::Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition");
+ transitionElem.setAttribute("target", ATTR(initialStates[i], "id"));
+ initialElem.appendChild(transitionElem);
+ _scxml.appendChild(initialElem);
+ initialTransitions.push_back(transitionElem);
+ }
+ }
+ enterStates(initialTransitions);
+
+ // assert(hasLegalConfiguration());
+ mainEventLoop();
+
+ if (_parentQueue) {
+ // send one final event to unblock eventual listeners
+ Event quit;
+ quit.name = "done.state.scxml";
+ _parentQueue->push(quit);
+ }
+
+ // set datamodel to null from this thread
+ if(_dataModel)
+ _dataModel = DataModel();
+
+}
+
+/**
+ * Called with a single data element from the topmost datamodel element.
+ */
+void InterpreterDraft7::initializeData(const Arabica::DOM::Node<std::string>& data) {
+ if (!_dataModel) {
+ LOG(ERROR) << "Cannot initialize data when no datamodel is given!";
+ return;
+ }
+ try {
+ if (!HAS_ATTR(data, "id")) {
+ LOG(ERROR) << "Data element has no id!";
+ return;
+ }
+
+ if (HAS_ATTR(data, "expr")) {
+ std::string value = ATTR(data, "expr");
+ _dataModel.assign(ATTR(data, "id"), value);
+ } else if (HAS_ATTR(data, "src")) {
+ URL srcURL(ATTR(data, "src"));
+ if (!srcURL.isAbsolute())
+ toAbsoluteURI(srcURL);
+
+ std::stringstream ss;
+ if (_cachedURLs.find(srcURL.asString()) != _cachedURLs.end()) {
+ ss << _cachedURLs[srcURL.asString()];
+ } else {
+ ss << srcURL;
+ _cachedURLs[srcURL.asString()] = srcURL;
+ }
+ _dataModel.assign(ATTR(data, "id"), ss.str());
+
+ } else if (data.hasChildNodes()) {
+ // search for the text node with the actual script
+ NodeList<std::string> dataChilds = data.getChildNodes();
+ for (int i = 0; i < dataChilds.getLength(); i++) {
+ if (dataChilds.item(i).getNodeType() == Node_base::TEXT_NODE) {
+ Data value = Data(dataChilds.item(i).getNodeValue());
+ _dataModel.assign(ATTR(data, "id"), value);
+ break;
+ }
+ }
+ }
+
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error in data element:" << std::endl << e << std::endl;
+ }
+}
+
+/**
+procedure mainEventLoop():
+ while running:
+ enabledTransitions = null
+ macrostepDone = false
+ # Here we handle eventless transitions and transitions
+ # triggered by internal events until macrostep is complete
+ while running and not macrostepDone:
+ enabledTransitions = selectEventlessTransitions()
+ if enabledTransitions.isEmpty():
+ if internalQueue.isEmpty():
+ macrostepDone = true
+ else:
+ internalEvent = internalQueue.dequeue()
+ datamodel["_event"] = internalEvent
+ enabledTransitions = selectTransitions(internalEvent)
+ if not enabledTransitions.isEmpty():
+ microstep(enabledTransitions.toList())
+ # either we're in a final state, and we break out of the loop
+ if not running:
+ break;
+ # or we've completed a macrostep, so we start a new macrostep by waiting for an external event
+ # Here we invoke whatever needs to be invoked. The implementation of 'invoke' is platform-specific
+ for state in statesToInvoke:
+ for inv in state.invoke:
+ invoke(inv)
+ statesToInvoke.clear()
+ # Invoking may have raised internal error events and we iterate to handle them
+ if not internalQueue.isEmpty():
+ continue
+ # A blocking wait for an external event. Alternatively, if we have been invoked
+ # our parent session also might cancel us. The mechanism for this is platform specific,
+ # but here we assume it’s a special event we receive
+ externalEvent = externalQueue.dequeue()
+ if isCancelEvent(externalEvent):
+ running = false
+ continue
+ datamodel["_event"] = externalEvent
+ for state in configuration:
+ for inv in state.invoke:
+ if inv.invokeid == externalEvent.invokeid:
+ applyFinalize(inv, externalEvent)
+ if inv.autoforward:
+ send(inv.id, externalEvent)
+ enabledTransitions = selectTransitions(externalEvent)
+ if not enabledTransitions.isEmpty():
+ microstep(enabledTransitions.toList())
+ # End of outer while running loop. If we get here, we have reached a top-level final state or have been cancelled
+ exitInterpreter()
+ */
+void InterpreterDraft7::mainEventLoop() {
+}
+
+/**
+procedure exitInterpreter():
+ statesToExit = configuration.toList().sort(exitOrder)
+ for s in statesToExit:
+ for content in s.onexit:
+ executeContent(content)
+ for inv in s.invoke:
+ cancelInvoke(inv)
+ configuration.delete(s)
+ if isFinalState(s) and isScxmlState(s.parent):
+ returnDoneEvent(s.donedata)
+ */
+void InterpreterDraft7::exitInterpreter() {
+}
+
+/**
+procedure microstep(enabledTransitions):
+ exitStates(enabledTransitions)
+ executeTransitionContent(enabledTransitions)
+ enterStates(enabledTransitions)
+ */
+void InterpreterDraft7::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+}
+
+/**
+function selectEventlessTransitions():
+ enabledTransitions = new OrderedSet()
+ atomicStates = configuration.toList().filter(isAtomicState).sort(documentOrder)
+ for state in atomicStates:
+ loop: for s in [state].append(getProperAncestors(state, null)):
+ for t in s.transition:
+ if not t.event and conditionMatch(t):
+ enabledTransitions.add(t)
+ break loop
+ enabledTransitions = filterPreempted(enabledTransitions)
+ return enabledTransitions
+ */
+Arabica::XPath::NodeSet<std::string> InterpreterDraft7::selectEventlessTransitions() {
+}
+
+/**
+function selectTransitions(event):
+ enabledTransitions = new OrderedSet()
+ atomicStates = configuration.toList().filter(isAtomicState).sort(documentOrder)
+ for state in atomicStates:
+ loop: for s in [state].append(getProperAncestors(state, null)):
+ for t in s.transition:
+ if t.event and nameMatch(t.event, event.name) and conditionMatch(t):
+ enabledTransitions.add(t)
+ break loop
+ enabledTransitions = removeConflictingTransitions(enabledTransitions)
+ return enabledTransitions
+ */
+Arabica::XPath::NodeSet<std::string> InterpreterDraft7::selectTransitions(const std::string& event) {
+}
+
+/**
+procedure enterStates(enabledTransitions):
+ statesToEnter = new OrderedSet()
+ statesForDefaultEntry = new OrderedSet()
+ computeEntrySet(enabledTransitions, statesToEnter, statesForDefaultEntry)
+ for s in statesToEnter.toList().sort(entryOrder):
+ configuration.add(s)
+ statesToInvoke.add(s)
+ if binding == "late" and s.isFirstEntry:
+ initializeDataModel(datamodel.s,doc.s)
+ s.isFirstEntry = false
+ for content in s.onentry:
+ executeContent(content)
+ if statesForDefaultEntry.isMember(s):
+ executeContent(s.initial.transition)
+ if isFinalState(s):
+ if isSCXMLElement(s.parent):
+ running = false
+ else:
+ parent = s.parent
+ grandparent = parent.parent
+ internalQueue.enqueue(new Event("done.state." + parent.id, s.donedata))
+ if isParallelState(grandparent):
+ if getChildStates(grandparent).every(isInFinalState):
+ internalQueue.enqueue(new Event("done.state." + grandparent.id))
+ */
+void InterpreterDraft7::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+}
+
+/**
+procedure exitStates(enabledTransitions):
+ statesToExit = computeExitSet(enabledTransitions)
+ for s in statesToExit:
+ statesToInvoke.delete(s)
+ statesToExit = statesToExit.toList().sort(exitOrder)
+ for s in statesToExit:
+ for h in s.history:
+ if h.type == "deep":
+ f = lambda s0: isAtomicState(s0) and isDescendant(s0,s)
+ else:
+ f = lambda s0: s0.parent == s
+ historyValue[h.id] = configuration.toList().filter(f)
+ for s in statesToExit:
+ for content in s.onexit:
+ executeContent(content)
+ for inv in s.invoke:
+ cancelInvoke(inv)
+ configuration.delete(s)
+ */
+void InterpreterDraft7::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+}
+
+/**
+function computeExitSet(transitions)
+ statesToExit = new OrderedSet
+ for t in transitions:
+ domain = getTransitionDomain(t)
+ for s in configuration:
+ if isDescendant(s,domain):
+ statesToExit.add(s)
+ return statesToExit
+ */
+Arabica::XPath::NodeSet<std::string> InterpreterDraft7::computeExitSet(const Arabica::XPath::NodeSet<std::string>& enabledTransitions,
+ const Arabica::XPath::NodeSet<std::string>& statesToExit) {
+}
+
+/**
+ procedure computeEntrySet(transitions, statesToEnter, statesForDefaultEntry)
+ for t in transitions:
+ statesToEnter.union(getTargetStates(t.target))
+ for s in statesToEnter:
+ addDescendentStatesToEnter(s,statesToEnter,statesForDefaultEntry)
+ for t in transitions:
+ ancestor = getTransitionDomain(t)
+ for s in getTargetStates(t.target)):
+ addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry)
+ */
+Arabica::XPath::NodeSet<std::string> InterpreterDraft7::computeEntrySet(const Arabica::XPath::NodeSet<std::string>& transitions,
+ const Arabica::XPath::NodeSet<std::string>& statesToEnter,
+ const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) {
+}
+
+/**
+function removeConflictingTransitions(enabledTransitions):
+ filteredTransitions = new OrderedSet()
+ // toList sorts the transitions in the order of the states that selected them
+ for t1 in enabledTransitions.toList():
+ t1Preempted = false;
+ transitionsToRemove = new OrderedSet()
+ for t2 in filteredTransitions.toList():
+ if computeExitSet(t1).hasIntersection(computeExitSet(t2)):
+ if isDescendent(t1.source, t2.source):
+ transitionsToRemove.add(t2)
+ else:
+ t1Preempted = true
+ break
+ if not t1Preempted:
+ for t3 in transitionsToRemove.toList():
+ filteredTransitions.delete(t3)
+ filteredTransitions.add(t1)
+
+ return filteredTransitions
+ */
+Arabica::XPath::NodeSet<std::string> InterpreterDraft7::removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+}
+
+/**
+ function getTransitionDomain(t)
+ tstates = getTargetStates(t.target)
+ if not tstates
+ return t.source
+ 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> InterpreterDraft7::getTransitionDomain(const Arabica::DOM::Node<std::string>& transition) {
+}
+
+/**
+ procedure addDescendentStatesToEnter(state,statesToEnter,statesForDefaultEntry):
+ if isHistoryState(state):
+ if historyValue[state.id]:
+ for s in historyValue[state.id]:
+ addDescendentStatesToEnter(s,statesToEnter,statesForDefaultEntry)
+ addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry)
+ else:
+ for t in state.transition:
+ for s in getTargetStates(t.target):
+ addDescendentStatesToEnter(s,statesToEnter,statesForDefaultEntry)
+ addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry)
+ else:
+ statesToEnter.add(state)
+ if isCompoundState(state):
+ statesForDefaultEntry.add(state)
+ for s in getTargetStates(state.initial):
+ addDescendentStatesToEnter(s,statesToEnter,statesForDefaultEntry)
+ addAncestorStatesToEnter(s, state, statesToEnter, statesForDefaultEntry)
+ else:
+ if isParallelState(state):
+ for child in getChildStates(state):
+ if not statesToEnter.some(lambda s: isDescendant(s,child)):
+ addDescendentStatesToEnter(child,statesToEnter,statesForDefaultEntry)
+ */
+Arabica::XPath::NodeSet<std::string> InterpreterDraft7::addDescendentStatesToEnter(const Arabica::DOM::Node<std::string>& state,
+ const Arabica::XPath::NodeSet<std::string>& statesToEnter,
+ const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) {
+}
+
+/**
+ procedure addAncestorsToEnter(state, ancestor, statesToEnter, statesForDefaultEntry)
+ 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)):
+ addStatesToEnter(child,statesToEnter,statesForDefaultEntry)
+ */
+Arabica::XPath::NodeSet<std::string> InterpreterDraft7::addAncestorsStatesToEnter(const Arabica::DOM::Node<std::string>& state,
+ const Arabica::DOM::Node<std::string>& ancestor,
+ const Arabica::XPath::NodeSet<std::string>& statesToEnter,
+ const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) {
+}
+
+
+} \ No newline at end of file
diff --git a/src/uscxml/interpreter/InterpreterDraft7.h b/src/uscxml/interpreter/InterpreterDraft7.h
new file mode 100644
index 0000000..e784e84
--- /dev/null
+++ b/src/uscxml/interpreter/InterpreterDraft7.h
@@ -0,0 +1,46 @@
+#ifndef INTERPRETERDRAFT7_H_WLJEI019
+#define INTERPRETERDRAFT7_H_WLJEI019
+
+#include "uscxml/Interpreter.h"
+
+namespace uscxml {
+
+class InterpreterDraft7 : public Interpreter {
+ void interpret();
+ void mainEventLoop();
+ void exitInterpreter();
+
+ void microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
+
+ Arabica::XPath::NodeSet<std::string> selectEventlessTransitions();
+ Arabica::XPath::NodeSet<std::string> selectTransitions(const std::string& event);
+
+ void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
+ void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
+
+ Arabica::XPath::NodeSet<std::string> computeExitSet(const Arabica::XPath::NodeSet<std::string>& enabledTransitions,
+ const Arabica::XPath::NodeSet<std::string>& statesToExit);
+
+ Arabica::XPath::NodeSet<std::string> computeEntrySet(const Arabica::XPath::NodeSet<std::string>& transitions,
+ const Arabica::XPath::NodeSet<std::string>& statesToEnter,
+ const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry);
+
+ Arabica::XPath::NodeSet<std::string> removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
+ Arabica::DOM::Node<std::string> getTransitionDomain(const Arabica::DOM::Node<std::string>& transition);
+
+ Arabica::XPath::NodeSet<std::string> addDescendentStatesToEnter(const Arabica::DOM::Node<std::string>& state,
+ const Arabica::XPath::NodeSet<std::string>& statesToEnter,
+ const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry);
+
+ Arabica::XPath::NodeSet<std::string> addAncestorsStatesToEnter(const Arabica::DOM::Node<std::string>& state,
+ const Arabica::DOM::Node<std::string>& ancestor,
+ const Arabica::XPath::NodeSet<std::string>& statesToEnter,
+ const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry);
+
+ void initializeData(const Arabica::DOM::Node<std::string>& data);
+
+};
+
+}
+
+#endif /* end of include guard: INTERPRETERDRAFT7_H_WLJEI019 */