summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/uscxml/CMakeLists.txt2
-rw-r--r--src/uscxml/Factory.cpp10
-rw-r--r--src/uscxml/Interpreter.cpp516
-rw-r--r--src/uscxml/Interpreter.h23
-rw-r--r--src/uscxml/interpreter/InterpreterDraft6.cpp533
-rw-r--r--src/uscxml/interpreter/InterpreterDraft6.h12
-rw-r--r--src/uscxml/interpreter/InterpreterRC.cpp579
-rw-r--r--src/uscxml/interpreter/InterpreterRC.cpp.deactivated1292
-rw-r--r--src/uscxml/interpreter/InterpreterRC.h (renamed from src/uscxml/interpreter/InterpreterRC.h.deactivated)37
-rw-r--r--src/uscxml/plugins/EventHandler.h5
-rw-r--r--src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp2
-rw-r--r--src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp2
-rw-r--r--src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp2
-rw-r--r--src/uscxml/transform/ChartToFSM.cpp2
14 files changed, 1124 insertions, 1893 deletions
diff --git a/src/uscxml/CMakeLists.txt b/src/uscxml/CMakeLists.txt
index 51ee4ea..979f8fd 100644
--- a/src/uscxml/CMakeLists.txt
+++ b/src/uscxml/CMakeLists.txt
@@ -85,7 +85,7 @@ list (APPEND USCXML_FILES ${USCXML_PLUGINS})
if (BUILD_AS_PLUGINS)
list (APPEND USCXML_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/plugins)
file(GLOB PLUMA
- plugins/Pluma/*.cpp
+ plugins/Pluma/*.cpp
plugins/Pluma/*.h
plugins/*.cpp
plugins/*.h
diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp
index 7117de2..4013c58 100644
--- a/src/uscxml/Factory.cpp
+++ b/src/uscxml/Factory.cpp
@@ -730,20 +730,20 @@ void EventHandlerImpl::returnErrorCommunication(const std::string& cause) {
returnEvent(exc);
}
-void EventHandlerImpl::returnEvent(Event& event, bool external) {
+void EventHandlerImpl::returnEvent(Event& event, bool internal) {
if (event.invokeid.length() == 0)
event.invokeid = _invokeId;
if (event.eventType == 0)
- event.eventType = (external ? Event::EXTERNAL : Event::INTERNAL);
+ event.eventType = (internal ? Event::INTERNAL : Event::EXTERNAL);
if (event.origin.length() == 0)
event.origin = "#_" + _invokeId;
if (event.origintype.length() == 0)
event.origintype = _type;
- if (external) {
- _interpreter->receive(event);
- } else {
+ if (internal) {
_interpreter->receiveInternal(event);
+ } else {
+ _interpreter->receive(event);
}
}
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index b9e7145..eface5e 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -47,14 +47,14 @@
#include <assert.h>
#include <algorithm>
-#include "uscxml/interpreter/InterpreterDraft6.h"
-//#include "uscxml/interpreter/InterpreterRC.h"
#include "uscxml/Factory.h"
#if 1
-#define INTERPRETER_IMPL InterpreterDraft6
+# define INTERPRETER_IMPL InterpreterDraft6
+# include "uscxml/interpreter/InterpreterDraft6.h"
#else
-#define INTERPRETER_IMPL InterpreterRC
+# define INTERPRETER_IMPL InterpreterRC
+# include "uscxml/interpreter/InterpreterRC.h"
#endif
#define VERBOSE 0
@@ -620,6 +620,498 @@ DONE_THREAD:
((InterpreterImpl*)instance)->_isStarted = false;
}
+void InterpreterImpl::exitInterpreter() {
+ NodeSet<std::string> statesToExit = _configuration;
+ statesToExit.forward(false);
+ statesToExit.sort();
+
+ for (int i = 0; i < statesToExit.size(); i++) {
+ Arabica::XPath::NodeSet<std::string> onExitElems = filterChildElements(_nsInfo.xmlNSPrefix + "onexit", statesToExit[i]);
+ for (int j = 0; j < onExitElems.size(); j++) {
+ executeContent(Element<std::string>(onExitElems[j]));
+ }
+ Arabica::XPath::NodeSet<std::string> invokeElems = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]);
+ // TODO: we ought to cancel all remaining invokers just to be sure with the persist extension
+ for (int j = 0; j < invokeElems.size(); j++) {
+ cancelInvoke(Element<std::string>(invokeElems[j]));
+ }
+ Element<std::string> stateElem(statesToExit[i]);
+ if (isFinal(stateElem) && parentIsScxmlState(stateElem)) {
+ returnDoneEvent(statesToExit[i]);
+ }
+ }
+ _configuration = NodeSet<std::string>();
+}
+
+
+InterpreterState InterpreterImpl::interpret() {
+ InterpreterState state;
+ while(true) {
+ state = step(-1);
+
+ switch (state) {
+ case uscxml::USCXML_FINISHED:
+ case uscxml::USCXML_DESTROYED:
+ // return as we finished
+ return state;
+ default:
+
+ // process invokers on main thread
+ if(_thread == NULL) {
+ runOnMainThread(200);
+ }
+
+ // process next step
+ break;
+ }
+ }
+ return state;
+}
+
+// setup / fetch the documents initial transitions
+NodeSet<std::string> InterpreterImpl::getDocumentInitialTransitions() {
+ NodeSet<std::string> initialTransitions;
+
+ if (_startConfiguration.size() > 0) {
+ // we emulate entering a given configuration by creating a pseudo deep history
+ Element<std::string> initHistory = _document.createElementNS(_nsInfo.nsURL, "history");
+ _nsInfo.setPrefix(initHistory);
+
+ initHistory.setAttribute("id", UUID::getUUID());
+ initHistory.setAttribute("type", "deep");
+ _scxml.insertBefore(initHistory, _scxml.getFirstChild());
+
+ std::string histId = ATTR(initHistory, "id");
+ NodeSet<std::string> histStates;
+ for (std::list<std::string>::const_iterator stateIter = _startConfiguration.begin(); stateIter != _startConfiguration.end(); stateIter++) {
+ histStates.push_back(getState(*stateIter));
+ }
+ _historyValue[histId] = histStates;
+
+ Element<std::string> initialElem = _document.createElementNS(_nsInfo.nsURL, "initial");
+ _nsInfo.setPrefix(initialElem);
+
+ initialElem.setAttribute("generated", "true");
+ Element<std::string> transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition");
+ _nsInfo.setPrefix(transitionElem);
+
+ transitionElem.setAttribute("target", histId);
+ initialElem.appendChild(transitionElem);
+ _scxml.appendChild(initialElem);
+ initialTransitions.push_back(transitionElem);
+
+ } else {
+ // try to get initial transition from initial element
+ initialTransitions = _xpath.evaluate("/" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", _scxml).asNodeSet();
+ if (initialTransitions.size() == 0) {
+ Arabica::XPath::NodeSet<std::string> initialStates;
+ // fetch per draft
+ initialStates = getInitialStates();
+ assert(initialStates.size() > 0);
+ for (int i = 0; i < initialStates.size(); i++) {
+ Element<std::string> initialElem = _document.createElementNS(_nsInfo.nsURL, "initial");
+ _nsInfo.setPrefix(initialElem);
+
+ initialElem.setAttribute("generated", "true");
+ Element<std::string> transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition");
+ _nsInfo.setPrefix(transitionElem);
+
+ transitionElem.setAttribute("target", ATTR_CAST(initialStates[i], "id"));
+ initialElem.appendChild(transitionElem);
+ _scxml.appendChild(initialElem);
+ initialTransitions.push_back(transitionElem);
+ }
+ }
+ }
+ return initialTransitions;
+}
+
+InterpreterState InterpreterImpl::step(int waitForMS) {
+ try {
+ tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
+
+ if (_state == USCXML_FINISHED || _state == USCXML_DESTROYED) {
+ return _state;
+ }
+
+ NodeSet<std::string> enabledTransitions;
+
+ // setup document and interpreter
+ if (!_isInitialized) {
+ init(); // will throw
+ }
+
+ if (_configuration.size() == 0) {
+ // goto initial configuration
+ NodeSet<std::string> initialTransitions = getDocumentInitialTransitions();
+ assert(initialTransitions.size() > 0);
+#if 1
+ std::cout << _name << ": initialTransitions: " << std::endl;
+ for (int i = 0; i < initialTransitions.size(); i++) {
+ std::cout << initialTransitions[i] << std::endl;
+ }
+ std::cout << std::endl;
+#endif
+
+ enterStates(initialTransitions);
+ setInterpreterState(USCXML_MICROSTEPPED);
+ }
+
+ assert(isLegalConfiguration(_configuration));
+
+ // are there spontaneous transitions?
+ if (!_stable) {
+ enabledTransitions = selectEventlessTransitions();
+ if (!enabledTransitions.empty()) {
+ // test 403b
+ enabledTransitions.to_document_order();
+ microstep(enabledTransitions);
+
+ setInterpreterState(USCXML_MICROSTEPPED);
+ return _state;
+ }
+ _stable = true;
+ }
+
+ // test415
+ if (_topLevelFinalReached)
+ goto EXIT_INTERPRETER;
+
+ // process internal event
+ if (!_internalQueue.empty()) {
+ _currEvent = _internalQueue.front();
+ _internalQueue.pop_front();
+ _stable = false;
+
+ USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent)
+
+ _dataModel.setEvent(_currEvent);
+ enabledTransitions = selectTransitions(_currEvent.name);
+
+ if (!enabledTransitions.empty()) {
+ // test 403b
+ enabledTransitions.to_document_order();
+ microstep(enabledTransitions);
+ }
+
+ // test 319 - even if we do not enable transitions, consider it a microstep
+ setInterpreterState(USCXML_MICROSTEPPED);
+ return _state;
+
+ } else {
+ _stable = true;
+ }
+
+ if (_state != USCXML_MACROSTEPPED && _state != USCXML_IDLE)
+ USCXML_MONITOR_CALLBACK(onStableConfiguration)
+
+ setInterpreterState(USCXML_MACROSTEPPED);
+
+ if (_topLevelFinalReached)
+ goto EXIT_INTERPRETER;
+
+
+ // when we reach a stable configuration, invoke
+ for (unsigned int i = 0; i < _statesToInvoke.size(); i++) {
+ NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]);
+ for (unsigned int j = 0; j < invokes.size(); j++) {
+ Element<std::string> invokeElem = Element<std::string>(invokes[j]);
+ if (!HAS_ATTR(invokeElem, "persist") || !DOMUtils::attributeIsTrue(ATTR(invokeElem, "persist"))) {
+ invoke(invokeElem);
+ }
+ }
+ }
+ _statesToInvoke = NodeSet<std::string>();
+
+ if (_externalQueue.isEmpty()) {
+ setInterpreterState(USCXML_IDLE);
+
+ if (waitForMS < 0) {
+ // wait blockingly for an event forever
+ while(_externalQueue.isEmpty()) {
+ _condVar.wait(_mutex);
+ }
+ }
+
+ if (waitForMS > 0) {
+ // wait given number of milliseconds max
+ uint64_t now = tthread::chrono::system_clock::now();
+ uint64_t then = now + waitForMS;
+ while(_externalQueue.isEmpty() && now < then) {
+ _condVar.wait_for(_mutex, then - now);
+ now = tthread::chrono::system_clock::now();
+ }
+ }
+
+ if (_externalQueue.isEmpty()) {
+ return _state;
+ }
+
+ setInterpreterState(USCXML_MACROSTEPPED);
+ }
+
+ _currEvent = _externalQueue.pop();
+ _currEvent.eventType = Event::EXTERNAL; // make sure it is set to external
+ _stable = false;
+
+ if (_topLevelFinalReached)
+ goto EXIT_INTERPRETER;
+
+ USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent)
+
+ if (iequals(_currEvent.name, "cancel.invoke." + _sessionId)) {
+ goto EXIT_INTERPRETER;
+ }
+
+ try {
+ _dataModel.setEvent(_currEvent);
+ } catch (Event e) {
+ LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl << _currEvent;
+ }
+
+ finalizeAndAutoForwardCurrentEvent();
+
+ // run internal processing until we reach a stable configuration again
+ enabledTransitions = selectTransitions(_currEvent.name);
+ if (!enabledTransitions.empty()) {
+ // test 403b
+ enabledTransitions.to_document_order();
+ microstep(enabledTransitions);
+ }
+
+ if (_topLevelFinalReached)
+ goto EXIT_INTERPRETER;
+
+ return _state;
+
+ EXIT_INTERPRETER:
+ USCXML_MONITOR_CALLBACK(beforeCompletion)
+
+ exitInterpreter();
+ if (_sendQueue) {
+ std::map<std::string, std::pair<InterpreterImpl*, SendRequest> >::iterator sendIter = _sendIds.begin();
+ while(sendIter != _sendIds.end()) {
+ _sendQueue->cancelEvent(sendIter->first);
+ sendIter++;
+ }
+ }
+
+ USCXML_MONITOR_CALLBACK(afterCompletion)
+
+ // assert(hasLegalConfiguration());
+ _mutex.unlock();
+
+ // remove datamodel
+ if(!_userSuppliedDataModel)
+ _dataModel = DataModel();
+
+ setInterpreterState(USCXML_FINISHED);
+ return _state;
+ } catch (boost::bad_weak_ptr e) {
+ LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl;
+ setInterpreterState(USCXML_DESTROYED);
+ return _state;
+ }
+
+ // set datamodel to null from this thread
+ if(_dataModel)
+ _dataModel = DataModel();
+
+}
+
+void InterpreterImpl::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+
+ USCXML_MONITOR_CALLBACK(beforeMicroStep)
+
+ exitStates(enabledTransitions);
+
+ for (int i = 0; i < enabledTransitions.size(); i++) {
+ Element<std::string> transition(enabledTransitions[i]);
+
+ USCXML_MONITOR_CALLBACK3(beforeTakingTransition, transition, (i + 1 < enabledTransitions.size()))
+
+ executeContent(transition);
+
+ USCXML_MONITOR_CALLBACK3(afterTakingTransition, transition, (i + 1 < enabledTransitions.size()))
+ }
+
+ enterStates(enabledTransitions);
+
+ USCXML_MONITOR_CALLBACK(afterMicroStep)
+
+}
+
+// process transitions until we are in a stable configuration again
+void InterpreterImpl::stabilize() {
+
+ NodeSet<std::string> enabledTransitions;
+ _stable = false;
+
+ if (_configuration.size() == 0) {
+ // goto initial configuration
+ NodeSet<std::string> initialTransitions = getDocumentInitialTransitions();
+ assert(initialTransitions.size() > 0);
+ enterStates(initialTransitions);
+ }
+
+ do { // process microsteps for enabled transitions until there are no more left
+
+ 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
+
+ USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent)
+
+ if (_dataModel)
+ _dataModel.setEvent(_currEvent);
+ enabledTransitions = selectTransitions(_currEvent.name);
+ }
+ }
+
+ if (!enabledTransitions.empty()) {
+ // test 403b
+ enabledTransitions.to_document_order();
+ microstep(enabledTransitions);
+ }
+ } while(!_internalQueue.empty() || !_stable);
+
+ USCXML_MONITOR_CALLBACK(onStableConfiguration)
+
+ // when we reach a stable configuration, invoke
+ for (unsigned int i = 0; i < _statesToInvoke.size(); i++) {
+ NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]);
+ for (unsigned int j = 0; j < invokes.size(); j++) {
+ Element<std::string> invokeElem = Element<std::string>(invokes[j]);
+ if (!HAS_ATTR(invokeElem, "persist") || !DOMUtils::attributeIsTrue(ATTR(invokeElem, "persist"))) {
+ invoke(invokeElem);
+ }
+ }
+ }
+ _statesToInvoke = NodeSet<std::string>();
+}
+
+Arabica::XPath::NodeSet<std::string> InterpreterImpl::selectTransitions(const std::string& event) {
+ Arabica::XPath::NodeSet<std::string> enabledTransitions;
+
+ NodeSet<std::string> states;
+ for (unsigned int i = 0; i < _configuration.size(); i++) {
+ if (isAtomic(Element<std::string>(_configuration[i])))
+ states.push_back(_configuration[i]);
+ }
+ states.to_document_order();
+
+ unsigned int index = 0;
+ while(states.size() > index) {
+ NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", states[index]);
+ for (unsigned int k = 0; k < transitions.size(); k++) {
+ if (isEnabledTransition(Element<std::string>(transitions[k]), event)) {
+ enabledTransitions.push_back(transitions[k]);
+ goto LOOP;
+ }
+ }
+ {
+ Node<std::string> parent = states[index].getParentNode();
+ if (parent) {
+ states.push_back(parent);
+ }
+ }
+ LOOP:
+ index++;
+ }
+
+ enabledTransitions = removeConflictingTransitions(enabledTransitions);
+ return enabledTransitions;
+}
+
+
+Arabica::XPath::NodeSet<std::string> InterpreterImpl::selectEventlessTransitions() {
+ Arabica::XPath::NodeSet<std::string> enabledTransitions;
+
+ NodeSet<std::string> states;
+ for (unsigned int i = 0; i < _configuration.size(); i++) {
+ if (isAtomic(Element<std::string>(_configuration[i])))
+ states.push_back(_configuration[i]);
+ }
+ states.to_document_order();
+
+#if 0
+ std::cout << "Atomic States: ";
+ for (int i = 0; i < atomicStates.size(); i++) {
+ std::cout << ATTR(atomicStates[i], "id") << ", ";
+ }
+ std::cout << std::endl;
+#endif
+
+ unsigned int index = 0;
+ while(states.size() > index) {
+ bool foundTransition = false;
+ NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", states[index]);
+ for (unsigned int k = 0; k < transitions.size(); k++) {
+ Element<std::string> transElem(transitions[k]);
+ if (!HAS_ATTR(transElem, "event") && hasConditionMatch(transElem)) {
+ enabledTransitions.push_back(transitions[k]);
+ foundTransition = true;
+ goto LOOP;
+ }
+ }
+ if (!foundTransition) {
+ Node<std::string> parent = states[index].getParentNode();
+ if (parent) {
+ states.push_back(parent);
+ }
+ }
+ LOOP:
+ index++;
+ }
+
+#if 0
+ std::cout << "Enabled eventless transitions: " << std::endl;
+ for (int i = 0; i < enabledTransitions.size(); i++) {
+ std::cout << enabledTransitions[i] << std::endl << "----" << std::endl;
+ }
+ std::cout << std::endl;
+#endif
+
+ enabledTransitions = removeConflictingTransitions(enabledTransitions);
+ return enabledTransitions;
+}
+
+
+bool InterpreterImpl::isEnabledTransition(const Element<std::string>& transition, const std::string& event) {
+ std::string eventName;
+ if (HAS_ATTR(transition, "event")) {
+ eventName = ATTR(transition, "event");
+ } else if(HAS_ATTR(transition, "eventexpr")) {
+ if (_dataModel) {
+ eventName = _dataModel.evalAsString(ATTR(transition, "eventexpr"));
+ } else {
+ LOG(ERROR) << "Transition has eventexpr attribute with no datamodel defined";
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ std::list<std::string> eventNames = tokenizeIdRefs(eventName);
+ std::list<std::string>::iterator eventIter = eventNames.begin();
+ while(eventIter != eventNames.end()) {
+ if(nameMatch(*eventIter, event) && hasConditionMatch(transition)) {
+ return true;
+ }
+ eventIter++;
+ }
+ return false;
+}
+
+
InterpreterState InterpreterImpl::getInterpreterState() {
return _state;
}
@@ -849,8 +1341,6 @@ void InterpreterImpl::init() {
_binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY);
- // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding
-
if (_binding == EARLY) {
// initialize all data elements
NodeSet<std::string> dataElems = _xpath.evaluate("//" + _nsInfo.xpathPrefix + "data", _scxml).asNodeSet();
@@ -2532,18 +3022,6 @@ bool InterpreterImpl::isAtomic(const Arabica::DOM::Element<std::string>& state)
if (iequals("parallel", LOCALNAME(state)))
return false;
-#if 0
- Arabica::DOM::Node<std::string> child = state.getFirstChild();
- while(child) {
- if (child.getNodeType() == Node_base::ELEMENT_NODE) {
- if (isState(Element<std::string>(child)))
- return false;
- }
- child = child.getNextSibling();
- }
-
-#else
-
Arabica::DOM::NodeList<std::string> childs = state.getChildNodes();
for (unsigned int i = 0; i < childs.getLength(); i++) {
if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE)
@@ -2552,8 +3030,6 @@ bool InterpreterImpl::isAtomic(const Arabica::DOM::Element<std::string>& state)
if (isState(Element<std::string>(childs.item(i))))
return false;
}
-#endif
-
return true;
}
diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h
index b2ea027..1f62255 100644
--- a/src/uscxml/Interpreter.h
+++ b/src/uscxml/Interpreter.h
@@ -232,12 +232,8 @@ public:
void copyTo(boost::shared_ptr<InterpreterImpl> other);
// TODO: We need to move the destructor to the implementations to make these pure virtual
- virtual InterpreterState interpret() {
- return _state; ///< Start interpreter blockingly
- }
- virtual InterpreterState step(int waitForMS = 0) {
- return _state;
- }; ///< Perform a single step
+ virtual InterpreterState interpret();
+ virtual InterpreterState step(int waitForMS = 0);
void start(); ///< Start interpretation in a thread
void stop(); ///< Stop interpreter thread
@@ -416,6 +412,7 @@ public:
Arabica::XPath::NodeSet<std::string> getStates(const std::list<std::string>& stateIds);
Arabica::XPath::NodeSet<std::string> getAllStates();
+ Arabica::XPath::NodeSet<std::string> getDocumentInitialTransitions();
Arabica::XPath::NodeSet<std::string> getInitialStates(Arabica::DOM::Element<std::string> state = Arabica::DOM::Element<std::string>());
static Arabica::XPath::NodeSet<std::string> getChildStates(const Arabica::DOM::Node<std::string>& state);
static Arabica::XPath::NodeSet<std::string> getChildStates(const Arabica::XPath::NodeSet<std::string>& state);
@@ -458,6 +455,13 @@ protected:
void initializeData(const Arabica::DOM::Element<std::string>& data);
void finalizeAndAutoForwardCurrentEvent();
+ void stabilize();
+ void microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
+ void exitInterpreter();
+
+ virtual Arabica::XPath::NodeSet<std::string> selectEventlessTransitions();
+ virtual Arabica::XPath::NodeSet<std::string> selectTransitions(const std::string& event);
+ virtual bool isEnabledTransition(const Arabica::DOM::Element<std::string>& transition, const std::string& event);
void setInterpreterState(InterpreterState newState);
@@ -467,6 +471,13 @@ protected:
tthread::condition_variable _condVar;
tthread::recursive_mutex _pluginMutex;
+ // to be overwritten by implementations - these ought to be pure, but impl destructor runs first
+ virtual void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {}
+ virtual void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {}
+ virtual Arabica::XPath::NodeSet<std::string> removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+ return enabledTransitions;
+ }
+
InterpreterState _state;
URL _baseURI;
URL _sourceURI;
diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp
index aedf516..7dcb768 100644
--- a/src/uscxml/interpreter/InterpreterDraft6.cpp
+++ b/src/uscxml/interpreter/InterpreterDraft6.cpp
@@ -33,460 +33,8 @@ using namespace Arabica::DOM;
// see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation
-InterpreterState InterpreterDraft6::interpret() {
- InterpreterState state;
- while(true) {
- state = step(-1);
-
- switch (state) {
- case uscxml::USCXML_FINISHED:
- case uscxml::USCXML_DESTROYED:
- // return as we finished
- return state;
- default:
-
- // process invokers on main thread
- if(_thread == NULL) {
- runOnMainThread(200);
- }
-
- // process next step
- break;
- }
- }
- return state;
-}
-
-// setup / fetch the documents initial transitions
-NodeSet<std::string> InterpreterDraft6::getDocumentInitialTransitions() {
- NodeSet<std::string> initialTransitions;
-
- if (_startConfiguration.size() > 0) {
- // we emulate entering a given configuration by creating a pseudo deep history
- Element<std::string> initHistory = _document.createElementNS(_nsInfo.nsURL, "history");
- _nsInfo.setPrefix(initHistory);
-
- initHistory.setAttribute("id", UUID::getUUID());
- initHistory.setAttribute("type", "deep");
- _scxml.insertBefore(initHistory, _scxml.getFirstChild());
-
- std::string histId = ATTR(initHistory, "id");
- NodeSet<std::string> histStates;
- for (std::list<std::string>::const_iterator stateIter = _startConfiguration.begin(); stateIter != _startConfiguration.end(); stateIter++) {
- histStates.push_back(getState(*stateIter));
- }
- _historyValue[histId] = histStates;
-
- Element<std::string> initialElem = _document.createElementNS(_nsInfo.nsURL, "initial");
- _nsInfo.setPrefix(initialElem);
-
- initialElem.setAttribute("generated", "true");
- Element<std::string> transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition");
- _nsInfo.setPrefix(transitionElem);
-
- transitionElem.setAttribute("target", histId);
- initialElem.appendChild(transitionElem);
- _scxml.appendChild(initialElem);
- initialTransitions.push_back(transitionElem);
-
- } else {
- // try to get initial transition from initial element
- initialTransitions = _xpath.evaluate("/" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", _scxml).asNodeSet();
- if (initialTransitions.size() == 0) {
- Arabica::XPath::NodeSet<std::string> initialStates;
- // fetch per draft
- initialStates = getInitialStates();
- assert(initialStates.size() > 0);
- for (int i = 0; i < initialStates.size(); i++) {
- Element<std::string> initialElem = _document.createElementNS(_nsInfo.nsURL, "initial");
- _nsInfo.setPrefix(initialElem);
-
- initialElem.setAttribute("generated", "true");
- Element<std::string> transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition");
- _nsInfo.setPrefix(transitionElem);
-
- transitionElem.setAttribute("target", ATTR_CAST(initialStates[i], "id"));
- initialElem.appendChild(transitionElem);
- _scxml.appendChild(initialElem);
- initialTransitions.push_back(transitionElem);
- }
- }
- }
- return initialTransitions;
-}
-
-InterpreterState InterpreterDraft6::step(int waitForMS = 0) {
- try {
- tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
-
- if (_state == USCXML_FINISHED || _state == USCXML_DESTROYED) {
- return _state;
- }
-
- NodeSet<std::string> enabledTransitions;
-
- // setup document and interpreter
- if (!_isInitialized) {
- init(); // will throw
- }
-
- if (_configuration.size() == 0) {
- // goto initial configuration
- NodeSet<std::string> initialTransitions = getDocumentInitialTransitions();
- assert(initialTransitions.size() > 0);
- enterStates(initialTransitions);
- setInterpreterState(USCXML_MICROSTEPPED);
- }
-
- assert(isLegalConfiguration(_configuration));
-
- // are there spontaneous transitions?
- if (!_stable) {
- enabledTransitions = selectEventlessTransitions();
- if (!enabledTransitions.empty()) {
- // test 403b
- enabledTransitions.to_document_order();
- microstep(enabledTransitions);
-
- setInterpreterState(USCXML_MICROSTEPPED);
- return _state;
- }
- _stable = true;
- }
-
- // test415
- if (_topLevelFinalReached)
- goto EXIT_INTERPRETER;
-
- // process internal event
- if (!_internalQueue.empty()) {
- _currEvent = _internalQueue.front();
- _internalQueue.pop_front();
- _stable = false;
-
- USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent)
-
- _dataModel.setEvent(_currEvent);
- enabledTransitions = selectTransitions(_currEvent.name);
-
- if (!enabledTransitions.empty()) {
- // test 403b
- enabledTransitions.to_document_order();
- microstep(enabledTransitions);
- }
-
- // test 319 - even if we do not enable transitions, consider it a microstep
- setInterpreterState(USCXML_MICROSTEPPED);
- return _state;
-
- } else {
- _stable = true;
- }
-
- if (_state != USCXML_MACROSTEPPED && _state != USCXML_IDLE)
- USCXML_MONITOR_CALLBACK(onStableConfiguration)
-
- setInterpreterState(USCXML_MACROSTEPPED);
-
- if (_topLevelFinalReached)
- goto EXIT_INTERPRETER;
-
-
- // when we reach a stable configuration, invoke
- for (unsigned int i = 0; i < _statesToInvoke.size(); i++) {
- NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]);
- for (unsigned int j = 0; j < invokes.size(); j++) {
- Element<std::string> invokeElem = Element<std::string>(invokes[j]);
- if (!HAS_ATTR(invokeElem, "persist") || !DOMUtils::attributeIsTrue(ATTR(invokeElem, "persist"))) {
- invoke(invokeElem);
- }
- }
- }
- _statesToInvoke = NodeSet<std::string>();
-
- if (_externalQueue.isEmpty()) {
- setInterpreterState(USCXML_IDLE);
-
- if (waitForMS < 0) {
- // wait blockingly for an event forever
- while(_externalQueue.isEmpty()) {
- _condVar.wait(_mutex);
- }
- }
-
- if (waitForMS > 0) {
- // wait given number of milliseconds max
- uint64_t now = tthread::chrono::system_clock::now();
- uint64_t then = now + waitForMS;
- while(_externalQueue.isEmpty() && now < then) {
- _condVar.wait_for(_mutex, then - now);
- now = tthread::chrono::system_clock::now();
- }
- }
-
- if (_externalQueue.isEmpty()) {
- return _state;
- }
-
- setInterpreterState(USCXML_MACROSTEPPED);
- }
-
- _currEvent = _externalQueue.pop();
- _currEvent.eventType = Event::EXTERNAL; // make sure it is set to external
- _stable = false;
-
- if (_topLevelFinalReached)
- goto EXIT_INTERPRETER;
-
- USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent)
-
- if (iequals(_currEvent.name, "cancel.invoke." + _sessionId)) {
- goto EXIT_INTERPRETER;
- }
-
- try {
- _dataModel.setEvent(_currEvent);
- } catch (Event e) {
- LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl << _currEvent;
- }
-
- finalizeAndAutoForwardCurrentEvent();
-
- // run internal processing until we reach a stable configuration again
- enabledTransitions = selectTransitions(_currEvent.name);
- if (!enabledTransitions.empty()) {
- // test 403b
- enabledTransitions.to_document_order();
- microstep(enabledTransitions);
- }
-
- if (_topLevelFinalReached)
- goto EXIT_INTERPRETER;
-
- return _state;
-
-EXIT_INTERPRETER:
- USCXML_MONITOR_CALLBACK(beforeCompletion)
-
- exitInterpreter();
- if (_sendQueue) {
- std::map<std::string, std::pair<InterpreterImpl*, SendRequest> >::iterator sendIter = _sendIds.begin();
- while(sendIter != _sendIds.end()) {
- _sendQueue->cancelEvent(sendIter->first);
- sendIter++;
- }
- }
-
- USCXML_MONITOR_CALLBACK(afterCompletion)
-
-// assert(hasLegalConfiguration());
- _mutex.unlock();
-
- // remove datamodel
- if(!_userSuppliedDataModel)
- _dataModel = DataModel();
-
- setInterpreterState(USCXML_FINISHED);
- return _state;
- } catch (boost::bad_weak_ptr e) {
- LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl;
- setInterpreterState(USCXML_DESTROYED);
- return _state;
- }
-
- // set datamodel to null from this thread
- if(_dataModel)
- _dataModel = DataModel();
-
-}
-
-// process transitions until we are in a stable configuration again
-void InterpreterDraft6::stabilize() {
-
- NodeSet<std::string> enabledTransitions;
- _stable = false;
-
- if (_configuration.size() == 0) {
- // goto initial configuration
- NodeSet<std::string> initialTransitions = getDocumentInitialTransitions();
- assert(initialTransitions.size() > 0);
- enterStates(initialTransitions);
- }
-
- do { // process microsteps for enabled transitions until there are no more left
-
- 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
-
- USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent)
-
- if (_dataModel)
- _dataModel.setEvent(_currEvent);
- enabledTransitions = selectTransitions(_currEvent.name);
- }
- }
-
- if (!enabledTransitions.empty()) {
- // test 403b
- enabledTransitions.to_document_order();
- microstep(enabledTransitions);
- }
- } while(!_internalQueue.empty() || !_stable);
-
- USCXML_MONITOR_CALLBACK(onStableConfiguration)
-
- // when we reach a stable configuration, invoke
- for (unsigned int i = 0; i < _statesToInvoke.size(); i++) {
- NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]);
- for (unsigned int j = 0; j < invokes.size(); j++) {
- Element<std::string> invokeElem = Element<std::string>(invokes[j]);
- if (!HAS_ATTR(invokeElem, "persist") || !DOMUtils::attributeIsTrue(ATTR(invokeElem, "persist"))) {
- invoke(invokeElem);
- }
- }
- }
- _statesToInvoke = NodeSet<std::string>();
-
-}
-
-Arabica::XPath::NodeSet<std::string> InterpreterDraft6::selectTransitions(const std::string& event) {
- Arabica::XPath::NodeSet<std::string> enabledTransitions;
-
- NodeSet<std::string> states;
- for (unsigned int i = 0; i < _configuration.size(); i++) {
- if (isAtomic(Element<std::string>(_configuration[i])))
- states.push_back(_configuration[i]);
- }
- states.to_document_order();
-
-#if 0
- std::cout << "Atomic states: " << std::endl;
- for (int i = 0; i < states.size(); i++) {
- std::cout << states[i] << std::endl << "----" << std::endl;
- }
- std::cout << std::endl;
-#endif
-
- unsigned int index = 0;
- while(states.size() > index) {
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", states[index]);
- for (unsigned int k = 0; k < transitions.size(); k++) {
- if (isEnabledTransition(Element<std::string>(transitions[k]), event)) {
- enabledTransitions.push_back(transitions[k]);
- goto LOOP;
- }
- }
- {
- Node<std::string> parent = states[index].getParentNode();
- if (parent) {
- states.push_back(parent);
- }
- }
-LOOP:
- index++;
- }
- enabledTransitions = filterPreempted(enabledTransitions);
-
-#if 0
- std::cout << "Enabled transitions: " << std::endl;
- for (int i = 0; i < enabledTransitions.size(); i++) {
- std::cout << DOMUtils::xPathForNode(enabledTransitions[i]) << std::endl;
- }
- std::cout << std::endl;
-#endif
-
- return enabledTransitions;
-}
-
-bool InterpreterDraft6::isEnabledTransition(const Element<std::string>& transition, const std::string& event) {
- std::string eventName;
- if (HAS_ATTR(transition, "event")) {
- eventName = ATTR(transition, "event");
- } else if(HAS_ATTR(transition, "eventexpr")) {
- if (_dataModel) {
- eventName = _dataModel.evalAsString(ATTR(transition, "eventexpr"));
- } else {
- LOG(ERROR) << "Transition has eventexpr attribute with no datamodel defined";
- return false;
- }
- } else {
- return false;
- }
-
- std::list<std::string> eventNames = tokenizeIdRefs(eventName);
- std::list<std::string>::iterator eventIter = eventNames.begin();
- while(eventIter != eventNames.end()) {
- if(nameMatch(*eventIter, event) && hasConditionMatch(transition)) {
- return true;
- }
- eventIter++;
- }
- return false;
-}
-
-Arabica::XPath::NodeSet<std::string> InterpreterDraft6::selectEventlessTransitions() {
- Arabica::XPath::NodeSet<std::string> enabledTransitions;
-
- NodeSet<std::string> states;
- for (unsigned int i = 0; i < _configuration.size(); i++) {
- if (isAtomic(Element<std::string>(_configuration[i])))
- states.push_back(_configuration[i]);
- }
- states.to_document_order();
-
-#if 0
- std::cout << "Atomic States: ";
- for (int i = 0; i < atomicStates.size(); i++) {
- std::cout << ATTR(atomicStates[i], "id") << ", ";
- }
- std::cout << std::endl;
-#endif
-
- unsigned int index = 0;
- while(states.size() > index) {
- bool foundTransition = false;
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", states[index]);
- for (unsigned int k = 0; k < transitions.size(); k++) {
- Element<std::string> transElem(transitions[k]);
- if (!HAS_ATTR(transElem, "event") && hasConditionMatch(transElem)) {
- enabledTransitions.push_back(transitions[k]);
- foundTransition = true;
- goto LOOP;
- }
- }
- if (!foundTransition) {
- Node<std::string> parent = states[index].getParentNode();
- if (parent) {
- states.push_back(parent);
- }
- }
-LOOP:
- index++;
- }
-
-#if 0
- std::cout << "Enabled eventless transitions: " << std::endl;
- for (int i = 0; i < enabledTransitions.size(); i++) {
- std::cout << enabledTransitions[i] << std::endl << "----" << std::endl;
- }
- std::cout << std::endl;
-#endif
-
- enabledTransitions = filterPreempted(enabledTransitions);
- return enabledTransitions;
-}
-
-Arabica::XPath::NodeSet<std::string> InterpreterDraft6::filterPreempted(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+Arabica::XPath::NodeSet<std::string> InterpreterDraft6::removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
Arabica::XPath::NodeSet<std::string> filteredTransitions;
for (unsigned int i = 0; i < enabledTransitions.size(); i++) {
Element<std::string> t(enabledTransitions[i]);
@@ -566,7 +114,6 @@ bool InterpreterDraft6::isWithinParallel(const Element<std::string>& transition)
_transWithinParallel[transition] = lcpa;
return _transWithinParallel[transition];
-
}
Node<std::string> InterpreterDraft6::findLCPA(const Arabica::XPath::NodeSet<std::string>& states) {
@@ -587,64 +134,6 @@ NEXT_ANCESTOR:
return ancestor;
}
-void InterpreterDraft6::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
-#if VERBOSE
- std::cout << "Transitions: ";
- for (int i = 0; i < enabledTransitions.size(); i++) {
- std::cout << ((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 << " " << ((Element<std::string>)targetSet[j]).getAttribute("id") << std::endl;
- }
- }
- std::cout << std::endl;
-#endif
-
- USCXML_MONITOR_CALLBACK(beforeMicroStep)
-
- exitStates(enabledTransitions);
-
- for (int i = 0; i < enabledTransitions.size(); i++) {
- Element<std::string> transition(enabledTransitions[i]);
-
- USCXML_MONITOR_CALLBACK3(beforeTakingTransition, transition, (i + 1 < enabledTransitions.size()))
-
- executeContent(transition);
-
- USCXML_MONITOR_CALLBACK3(afterTakingTransition, transition, (i + 1 < enabledTransitions.size()))
- }
-
- enterStates(enabledTransitions);
-
- USCXML_MONITOR_CALLBACK(afterMicroStep)
-
-}
-
-void InterpreterDraft6::exitInterpreter() {
-#if VERBOSE
- std::cout << "Exiting interpreter " << _name << std::endl;
-#endif
- NodeSet<std::string> statesToExit = _configuration;
- statesToExit.forward(false);
- statesToExit.sort();
-
- for (int i = 0; i < statesToExit.size(); i++) {
- Arabica::XPath::NodeSet<std::string> onExitElems = filterChildElements(_nsInfo.xmlNSPrefix + "onexit", statesToExit[i]);
- for (int j = 0; j < onExitElems.size(); j++) {
- executeContent(Element<std::string>(onExitElems[j]));
- }
- Arabica::XPath::NodeSet<std::string> invokeElems = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]);
- // TODO: we ought to cancel all remaining invokers just to be sure with the persist extension
- for (int j = 0; j < invokeElems.size(); j++) {
- cancelInvoke(Element<std::string>(invokeElems[j]));
- }
- Element<std::string> stateElem(statesToExit[i]);
- if (isFinal(stateElem) && parentIsScxmlState(stateElem)) {
- returnDoneEvent(statesToExit[i]);
- }
- }
- _configuration = NodeSet<std::string>();
-}
void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
@@ -737,7 +226,7 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e
#if VERBOSE
std::cout << _name << ": History node " << ATTR(historyElem, "id") << " contains: ";
for (int i = 0; i < historyNodes.size(); i++) {
- std::cout << ATTR(historyNodes[i], "id") << ", ";
+ std::cout << ATTR_CAST(historyNodes[i], "id") << ", ";
}
std::cout << std::endl;
#endif
@@ -801,7 +290,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
#if VERBOSE
std::cout << _name << ": Target States: ";
for (int i = 0; i < tStates.size(); i++) {
- std::cout << ATTR(tStates[i], "id") << ", ";
+ std::cout << ATTR_CAST(tStates[i], "id") << ", ";
}
std::cout << std::endl;
#endif
@@ -809,7 +298,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
Node<std::string> ancestor;
Node<std::string> source = getSourceState(transition);
#if VERBOSE
- std::cout << _name << ": Source States: " << ATTR(source, "id") << std::endl;
+ std::cout << _name << ": Source States: " << ATTR_CAST(source, "id") << std::endl;
#endif
assert(source);
@@ -833,7 +322,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
}
#if VERBOSE
- std::cout << _name << ": Ancestor: " << ATTR(ancestor, "id") << std::endl;
+ std::cout << _name << ": Ancestor: " << ATTR_CAST(ancestor, "id") << std::endl;
#endif
for (int j = 0; j < tStates.size(); j++) {
@@ -843,7 +332,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
#if VERBOSE
std::cout << _name << ": States to enter: ";
for (int i = 0; i < statesToEnter.size(); i++) {
- std::cout << LOCALNAME(statesToEnter[i]) << ":" << ATTR(statesToEnter[i], "id") << ", ";
+ std::cout << LOCALNAME(statesToEnter[i]) << ":" << ATTR_CAST(statesToEnter[i], "id") << ", ";
}
std::cout << std::endl;
#endif
@@ -852,9 +341,9 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
NodeSet<std::string> ancestors = getProperAncestors(tStates[j], ancestor);
#if VERBOSE
- std::cout << _name << ": Proper Ancestors of " << ATTR(tStates[j], "id") << " and " << ATTR(ancestor, "id") << ": ";
+ std::cout << _name << ": Proper Ancestors of " << ATTR_CAST(tStates[j], "id") << " and " << ATTR_CAST(ancestor, "id") << ": ";
for (int i = 0; i < ancestors.size(); i++) {
- std::cout << ATTR(ancestors[i], "id") << ", ";
+ std::cout << ATTR_CAST(ancestors[i], "id") << ", ";
}
std::cout << std::endl;
#endif
@@ -885,7 +374,7 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
#if VERBOSE
std::cout << _name << ": States to enter: ";
for (int i = 0; i < statesToEnter.size(); i++) {
- std::cout << ATTR(statesToEnter[i], "id") << ", ";
+ std::cout << ATTR_CAST(statesToEnter[i], "id") << ", ";
}
std::cout << std::endl;
#endif
@@ -998,7 +487,7 @@ void InterpreterDraft6::addStatesToEnter(const Element<std::string>& state,
#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 << ATTR_CAST(historyValue[i], "id") << ", ";
}
std::cout << std::endl;
#endif
@@ -1010,7 +499,7 @@ void InterpreterDraft6::addStatesToEnter(const Element<std::string>& state,
#if VERBOSE
std::cout << "Proper Ancestors: ";
for (int i = 0; i < ancestors.size(); i++) {
- std::cout << ATTR(ancestors[i], "id") << ", ";
+ std::cout << ATTR_CAST(ancestors[i], "id") << ", ";
}
std::cout << std::endl;
#endif
diff --git a/src/uscxml/interpreter/InterpreterDraft6.h b/src/uscxml/interpreter/InterpreterDraft6.h
index 3414e5e..6a1275b 100644
--- a/src/uscxml/interpreter/InterpreterDraft6.h
+++ b/src/uscxml/interpreter/InterpreterDraft6.h
@@ -29,11 +29,7 @@ public:
virtual ~InterpreterDraft6() {};
protected:
- virtual InterpreterState interpret();
- virtual InterpreterState step(int blocking);
- void stabilize();
- void microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
void addStatesToEnter(const Arabica::DOM::Element<std::string>& state,
Arabica::XPath::NodeSet<std::string>& statesToEnter,
@@ -41,15 +37,9 @@ protected:
Arabica::XPath::NodeSet<std::string>& defaultHistoryContent);
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);
+ Arabica::XPath::NodeSet<std::string> removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
bool isPreemptingTransition(const Arabica::DOM::Element<std::string>& t1, const Arabica::DOM::Element<std::string>& t2);
- bool isEnabledTransition(const Arabica::DOM::Element<std::string>& transition, const std::string& event);
-
- Arabica::XPath::NodeSet<std::string> getDocumentInitialTransitions();
bool isCrossingBounds(const Arabica::DOM::Element<std::string>& transition);
bool isWithinParallel(const Arabica::DOM::Element<std::string>& transition);
diff --git a/src/uscxml/interpreter/InterpreterRC.cpp b/src/uscxml/interpreter/InterpreterRC.cpp
new file mode 100644
index 0000000..6aea740
--- /dev/null
+++ b/src/uscxml/interpreter/InterpreterRC.cpp
@@ -0,0 +1,579 @@
+/**
+ * @file
+ * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#include "InterpreterRC.h"
+
+#include "uscxml/Factory.h"
+#include "uscxml/concurrency/DelayedEventQueue.h"
+
+#include <glog/logging.h>
+#include "uscxml/UUID.h"
+#include "uscxml/DOMUtils.h"
+
+namespace uscxml {
+
+using namespace Arabica::XPath;
+using namespace Arabica::DOM;
+
+size_t padding = 0;
+
+std::string getPadding() {
+ std::string pad = "";
+ for (int i = 0; i < padding; i++) {
+ pad += " ";
+ }
+ return pad;
+}
+
+Arabica::XPath::NodeSet<std::string> InterpreterRC::removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+ Arabica::XPath::NodeSet<std::string> filteredTransitions;
+
+ for (unsigned int i = 0; i < enabledTransitions.size(); i++) {
+ Element<std::string> t1(enabledTransitions[i]);
+ bool t1Preempted = false;
+ Arabica::XPath::NodeSet<std::string> transitionsToRemove;
+
+ for (unsigned int j = 0; j < filteredTransitions.size(); j++) {
+ Element<std::string> t2(enabledTransitions[j]);
+ if (hasIntersection(computeExitSet(t1), computeExitSet(t2))) {
+ if (isDescendant(getSourceState(t1), getSourceState(t2))) {
+ transitionsToRemove.push_back(t2);
+ } else {
+ t1Preempted = true;
+ break;
+ }
+ }
+ }
+
+ if (!t1Preempted) {
+ // remove transitionsToRemove from filteredTransitions
+ std::list<Node<std::string> > tmp;
+ for (int i = 0; i < filteredTransitions.size(); i++) {
+ if (!isMember(filteredTransitions[i], transitionsToRemove)) {
+ tmp.push_back(filteredTransitions[i]);
+ }
+ }
+ filteredTransitions = NodeSet<std::string>();
+ filteredTransitions.insert(filteredTransitions.end(), tmp.begin(), tmp.end());
+
+ filteredTransitions.push_back(t1);
+ }
+ }
+ return filteredTransitions;
+}
+
+bool InterpreterRC::hasIntersection(const Arabica::XPath::NodeSet<std::string>& nodeSet1, const Arabica::XPath::NodeSet<std::string>& nodeSet2) {
+ for (unsigned int i = 0; i < nodeSet1.size(); i++) {
+ for (unsigned int j = 0; j < nodeSet2.size(); j++) {
+ if (nodeSet1[i] == nodeSet2[j])
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void InterpreterRC::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+ NodeSet<std::string> statesToExit = computeExitSet(enabledTransitions);
+
+ // remove statesToExit from _statesToInvoke
+ std::list<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.forward(false);
+ statesToExit.sort();
+
+ for (int i = 0; i < statesToExit.size(); i++) {
+ NodeSet<std::string> histories = filterChildElements(_nsInfo.xmlNSPrefix + "history", statesToExit[i]);
+ for (int j = 0; j < histories.size(); j++) {
+ Element<std::string> historyElem = (Element<std::string>)histories[j];
+ std::string historyType = (historyElem.hasAttribute("type") ? historyElem.getAttribute("type") : "shallow");
+ NodeSet<std::string> historyNodes;
+ for (int k = 0; k < _configuration.size(); k++) {
+ if (iequals(historyType, "deep")) {
+ if (isAtomic(Element<std::string>(_configuration[k])) && isDescendant(_configuration[k], statesToExit[i]))
+ historyNodes.push_back(_configuration[k]);
+ } else {
+ if (_configuration[k].getParentNode() == statesToExit[i])
+ historyNodes.push_back(_configuration[k]);
+ }
+ }
+ _historyValue[historyElem.getAttribute("id")] = historyNodes;
+ }
+ }
+
+ for (int i = 0; i < statesToExit.size(); i++) {
+ USCXML_MONITOR_CALLBACK3(beforeExitingState, Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size()))
+
+ NodeSet<std::string> onExits = filterChildElements(_nsInfo.xmlNSPrefix + "onExit", statesToExit[i]);
+ for (int j = 0; j < onExits.size(); j++) {
+ Element<std::string> onExitElem = (Element<std::string>)onExits[j];
+ executeContent(onExitElem);
+ }
+
+ USCXML_MONITOR_CALLBACK3(afterExitingState, Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size()))
+
+ NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]);
+ for (int j = 0; j < invokes.size(); j++) {
+ Element<std::string> invokeElem = (Element<std::string>)invokes[j];
+ cancelInvoke(invokeElem);
+ }
+
+ // remove statesToExit[i] from _configuration - test409
+ tmp.clear();
+ for (int j = 0; j < _configuration.size(); j++) {
+ if (_configuration[j] != statesToExit[i]) {
+ tmp.push_back(_configuration[j]);
+ }
+ }
+ _configuration = NodeSet<std::string>();
+ _configuration.insert(_configuration.end(), tmp.begin(), tmp.end());
+ }
+}
+
+
+Arabica::XPath::NodeSet<std::string> InterpreterRC::computeExitSet(const Arabica::XPath::NodeSet<std::string>& transitions) {
+ NodeSet<std::string> statesToExit;
+ for (unsigned int i = 0; i < transitions.size(); i++) {
+ Element<std::string> t(transitions[i]);
+ if (isTargetless(t))
+ continue;
+ Arabica::DOM::Node<std::string> domain = getTransitionDomain(t);
+ if (!domain)
+ continue;
+ for (unsigned int j = 0; j < _configuration.size(); j++) {
+ const Node<std::string>& s = _configuration[j];
+ if (isDescendant(s, domain)) {
+ statesToExit.push_back(s);
+ }
+ }
+ }
+#if 0
+ std::cout << "computeExitSet: ";
+ for (int i = 0; i < statesToExit.size(); i++) {
+ std::cout << ATTR(statesToExit[i], "id") << " ";
+ }
+ std::cout << std::endl;
+#endif
+ return statesToExit;
+}
+
+Arabica::XPath::NodeSet<std::string> InterpreterRC::computeExitSet(const Arabica::DOM::Node<std::string>& transition) {
+ Arabica::XPath::NodeSet<std::string> transitions;
+ transitions.push_back(transition);
+ return computeExitSet(transitions);
+}
+
+
+void InterpreterRC::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
+ NodeSet<std::string> statesToEnter;
+ NodeSet<std::string> statesForDefaultEntry;
+ // initialize the temporary table for default content in history states
+ std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent;
+
+ computeEntrySet(enabledTransitions, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
+ statesToEnter.to_document_order();
+
+ for (int i = 0; i < statesToEnter.size(); i++) {
+ Element<std::string> s = (Element<std::string>)statesToEnter[i];
+
+ USCXML_MONITOR_CALLBACK3(beforeEnteringState, s, i + 1 < statesToEnter.size())
+
+ _configuration.push_back(s);
+ _statesToInvoke.push_back(s);
+
+ // if (_binding == LATE && stateElem.getAttribute("isFirstEntry").size() > 0) {
+ if (_binding == LATE && !isMember(s, _alreadyEntered)) {
+ NodeSet<std::string> dataModelElems = filterChildElements(_nsInfo.xmlNSPrefix + "datamodel", s);
+ if(dataModelElems.size() > 0 && _dataModel) {
+ Arabica::XPath::NodeSet<std::string> dataElems = filterChildElements(_nsInfo.xmlNSPrefix + "data", dataModelElems[0]);
+ for (int j = 0; j < dataElems.size(); j++) {
+ if (dataElems[j].getNodeType() == Node_base::ELEMENT_NODE)
+ initializeData(Element<std::string>(dataElems[j]));
+ }
+ }
+ _alreadyEntered.push_back(s);
+ // stateElem.setAttribute("isFirstEntry", "");
+ }
+ // execute onentry executable content
+ NodeSet<std::string> onEntryElems = filterChildElements(_nsInfo.xmlNSPrefix + "onEntry", s);
+ executeContent(onEntryElems, false);
+
+ USCXML_MONITOR_CALLBACK3(afterEnteringState, s, i + 1 < statesToEnter.size())
+
+ if (isMember(s, statesForDefaultEntry)) {
+ // execute initial transition content for compound states
+ Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", s).asNodeSet();
+ executeContent(transitions);
+ }
+ if (defaultHistoryContent.find(ATTR(s, "id")) != defaultHistoryContent.end()) {
+ executeContent(Element<std::string>(defaultHistoryContent[ATTR(s, "id")]));
+ }
+
+ if (isFinal(s)) {
+ internalDoneSend(s);
+ if (parentIsScxmlState(s)) {
+ _topLevelFinalReached = true;
+ } else {
+ Element<std::string> parent = (Element<std::string>)s.getParentNode();
+ Element<std::string> grandParent = (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(Element<std::string>(childs[j]))) {
+ inFinalState = false;
+ break;
+ }
+ }
+ if (inFinalState) {
+ internalDoneSend(grandParent);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void InterpreterRC::computeEntrySet(const Arabica::XPath::NodeSet<std::string>& transitions,
+ NodeSet<std::string>& statesToEnter,
+ NodeSet<std::string>& statesForDefaultEntry,
+ std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent) {
+
+#if 1
+ for (int i = 0; i < transitions.size(); i++) {
+ Element<std::string> t(transitions[i]);
+
+ NodeSet<std::string> targets = getTargetStates(t);
+
+#if 1
+ std::cout << "computeEntrySet: ";
+ for (int i = 0; i < targets.size(); i++) {
+ std::cout << ATTR_CAST(targets[i], "id") << " ";
+ }
+ std::cout << std::endl;
+#endif
+
+
+ for (int j = 0; j < targets.size(); j++) {
+ Element<std::string> s = Element<std::string>(targets[j]);
+ addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
+ }
+
+ for (int j = 0; j < targets.size(); j++) {
+ Element<std::string> s = Element<std::string>(targets[j]);
+ addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
+ }
+
+#if 1
+ std::cout << "after addDescendantStatesToEnter: ";
+ for (int i = 0; i < statesToEnter.size(); i++) {
+ std::cout << ATTR_CAST(statesToEnter[i], "id") << " ";
+ }
+ std::cout << std::endl;
+#endif
+
+ Element<std::string> ancestor = Element<std::string>(getTransitionDomain(t));
+ NodeSet<std::string> effectiveTargetStates = getEffectiveTargetStates(t);
+
+ for (int j = 0; j < effectiveTargetStates.size(); j++) {
+ Element<std::string> s = Element<std::string>(effectiveTargetStates[j]);
+ addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
+ }
+
+#if 1
+ std::cout << "after addAncestorStatesToEnter: ";
+ for (int i = 0; i < statesToEnter.size(); i++) {
+ std::cout << ATTR_CAST(statesToEnter[i], "id") << " ";
+ }
+ std::cout << std::endl;
+#endif
+
+ }
+
+
+
+
+#else
+ for (int i = 0; i < transitions.size(); i++) {
+ Element<std::string> t(transitions[i]);
+
+ NodeSet<std::string> targets = getTargetStates(t);
+ for (int j = 0; j < targets.size(); j++) {
+ if (!isMember(targets[j], statesToEnter)) {
+ std::cout << "adding: " << ATTR_CAST(anc, "id") << std::endl;
+ statesToEnter.push_back(targets[j]);
+ }
+ }
+ }
+
+#if 1
+ std::cout << "before addDescendantStatesToEnter: ";
+ for (int i = 0; i < statesToEnter.size(); i++) {
+ std::cout << ATTR_CAST(statesToEnter[i], "id") << " ";
+ }
+ std::cout << std::endl;
+#endif
+
+ NodeSet<std::string> tmp = statesToEnter;
+ for (int i = 0; i < tmp.size(); i++) {
+ assert(tmp[i]);
+ addDescendantStatesToEnter(tmp[i],statesToEnter,statesForDefaultEntry, defaultHistoryContent);
+ }
+
+#if 1
+ std::cout << "after addDescendantStatesToEnter: ";
+ for (int i = 0; i < statesToEnter.size(); i++) {
+ std::cout << ATTR_CAST(statesToEnter[i], "id") << " ";
+ }
+ std::cout << std::endl;
+#endif
+
+ for (int i = 0; i < transitions.size(); i++) {
+ Element<std::string> t = (Element<std::string>)transitions[i];
+ Node<std::string> ancestor = getTransitionDomain(t);
+ NodeSet<std::string> targets = getTargetStates(t);
+ for (int j = 0; j < targets.size(); j++) {
+ const Node<std::string>& s = targets[j];
+ addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
+ }
+ }
+
+#if 1
+ std::cout << "after addAncestorStatesToEnter: ";
+ for (int i = 0; i < statesToEnter.size(); i++) {
+ std::cout << ATTR_CAST(statesToEnter[i], "id") << " ";
+ }
+ std::cout << std::endl;
+#endif
+
+#endif
+}
+
+Arabica::XPath::NodeSet<std::string> InterpreterRC::getEffectiveTargetStates(const Arabica::DOM::Element<std::string>& transition) {
+ NodeSet<std::string> effectiveTargets;
+ NodeSet<std::string> targets = getTargetStates(transition);
+
+ for (int j = 0; j < targets.size(); j++) {
+ Element<std::string> s = Element<std::string>(targets[j]);
+ if (isHistory(s)) {
+ if (_historyValue.find(ATTR(s, "id")) != _historyValue.end()) {
+ targets.push_back(_historyValue["id"]);
+ } else {
+ NodeSet<std::string> histTrans = filterChildElements(_nsInfo.xmlNSPrefix + "transition", s);
+ // TODO: what if there are many history transitions?
+ if (histTrans.size() > 0)
+ targets.push_back(getEffectiveTargetStates(Element<std::string>(histTrans[0])));
+ }
+ } else {
+ effectiveTargets.push_back(s);
+ }
+ }
+
+ return effectiveTargets;
+}
+
+void InterpreterRC::computeEntrySet(const Arabica::DOM::Node<std::string>& transition,
+ NodeSet<std::string>& statesToEnter,
+ NodeSet<std::string>& statesForDefaultEntry,
+ std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent) {
+ Arabica::XPath::NodeSet<std::string> transitions;
+ transitions.push_back(transition);
+ computeEntrySet(transitions, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
+}
+
+void InterpreterRC::addDescendantStatesToEnter(const Arabica::DOM::Element<std::string>& state,
+ Arabica::XPath::NodeSet<std::string>& statesToEnter,
+ Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
+ std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent) {
+
+ std::cout << getPadding() << "addDescendantStatesToEnter: " << ATTR(state, "id") << std::endl;
+ padding++;
+
+ if (isHistory(state)) {
+
+ std::string stateId = ATTR(state, "id");
+ if (_historyValue.find(stateId) != _historyValue.end()) {
+ const Arabica::XPath::NodeSet<std::string>& historyValue = _historyValue[stateId];
+ for (int i = 0; i < historyValue.size(); i++) {
+ const Element<std::string>& s = Element<std::string>(historyValue[i]);
+ addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
+ addAncestorStatesToEnter(s, getParentState(s), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
+ }
+
+ } else {
+ NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", state);
+ if (transitions.size() > 0) {
+ // TODO: what if there are many history transitions?
+ defaultHistoryContent[stateId] = transitions[0];
+ }
+
+ for (int i = 0; i < transitions.size(); i++) {
+ NodeSet<std::string> targets = getTargetStates(Element<std::string>(transitions[i]));
+ for (int j = 0; j < targets.size(); j++) {
+ const Element<std::string>& s = Element<std::string>(targets[i]);
+ addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
+ addAncestorStatesToEnter(s, getParentState(s), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
+ }
+ }
+ }
+ } else {
+
+ if (!isMember(state, statesToEnter)) { // adding an existing element invalidates old reference
+ std::cout << getPadding() << "adding: " << ATTR_CAST(state, "id") << std::endl;
+ statesToEnter.push_back(state);
+ }
+
+ if (isCompound(state)) {
+ statesForDefaultEntry.push_back(state);
+
+ NodeSet<std::string> targets = getInitialStates(Element<std::string>(state));
+ for (int i = 0; i < targets.size(); i++) {
+ const Element<std::string>& s = Element<std::string>(targets[i]);
+ addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
+ }
+
+ for (int i = 0; i < targets.size(); i++) {
+ const Element<std::string>& s = Element<std::string>(targets[i]);
+ addAncestorStatesToEnter(s, state, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
+ }
+
+ } else if(isParallel(state)) {
+ NodeSet<std::string> childStates = getChildStates(state);
+
+ for (int i = 0; i < childStates.size(); i++) {
+ const Element<std::string>& child = Element<std::string>(childStates[i]);
+
+ for (int j = 0; j < statesToEnter.size(); j++) {
+ const Node<std::string>& s = statesToEnter[j];
+ if (isDescendant(s, child)) {
+ goto BREAK_LOOP;
+ }
+
+ }
+ addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
+BREAK_LOOP:
+ ;
+ }
+ }
+ }
+ padding--;
+}
+
+void InterpreterRC::addAncestorStatesToEnter(const Arabica::DOM::Element<std::string>& state,
+ const Arabica::DOM::Element<std::string>& ancestor,
+ Arabica::XPath::NodeSet<std::string>& statesToEnter,
+ Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
+ std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent) {
+
+ std::cout << getPadding() << "addAncestorStatesToEnter: " << ATTR(state, "id") << " - " << ATTR(ancestor, "id") << std::endl;
+ padding++;
+
+ NodeSet<std::string> ancestors = getProperAncestors(state, ancestor);
+ for (int i = 0; i < ancestors.size(); i++) {
+ const Node<std::string>& anc = ancestors[i];
+ std::cout << getPadding() << "adding: " << ATTR_CAST(anc, "id") << std::endl;
+ statesToEnter.push_back(anc);
+ if (isParallel(Element<std::string>(anc))) {
+ NodeSet<std::string> childStates = getChildStates(anc);
+ for (int j = 0; j < childStates.size(); j++) {
+ const Element<std::string>& child = Element<std::string>(childStates[j]);
+ for (int k = 0; k < statesToEnter.size(); k++) {
+ const Node<std::string>& s = statesToEnter[k];
+ if (isDescendant(s, child)) {
+ goto BREAK_LOOP;
+ }
+ }
+ addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
+BREAK_LOOP:
+ ;
+ }
+ }
+ }
+ padding--;
+}
+
+
+Arabica::DOM::Node<std::string> InterpreterRC::getTransitionDomain(const Arabica::DOM::Element<std::string>& transition) {
+#if 1
+
+ NodeSet<std::string> tStates = getEffectiveTargetStates(transition);
+ Node<std::string> source = getSourceState(transition);
+
+ if (tStates.size() == 0) {
+ return Arabica::DOM::Node<std::string>(); // null
+ }
+ std::string transitionType = (HAS_ATTR(transition, "type") ? ATTR(transition, "type") : "external");
+
+ if (iequals(transitionType, "internal") && isCompound(Element<std::string>(source))) {
+ for (int i = 0; i < tStates.size(); i++) {
+ const Node<std::string>& s = tStates[i];
+ if (!isDescendant(s, source))
+ goto BREAK_LOOP;
+ }
+ return source;
+ }
+
+BREAK_LOOP:
+ Arabica::XPath::NodeSet<std::string> states;
+ states.push_back(source);
+ states.push_back(tStates);
+ return findLCCA(states);
+
+#else
+ NodeSet<std::string> tStates = getTargetStates(transition);
+ Node<std::string> source = getSourceState(transition);
+
+#if 0
+ std::cout << "getTransitionDomain: " << std::endl << transition << std::endl;
+#endif
+
+ if (tStates.size() == 0) {
+ return Arabica::DOM::Node<std::string>(); // null
+ }
+ std::string transitionType = (HAS_ATTR(transition, "type") ? ATTR(transition, "type") : "external");
+
+ if (iequals(transitionType, "internal") && isCompound(Element<std::string>(source))) {
+ for (int i = 0; i < tStates.size(); i++) {
+ const Node<std::string>& s = tStates[i];
+ if (!isDescendant(s, source))
+ goto BREAK_LOOP;
+ }
+ return source;
+ }
+BREAK_LOOP:
+ ;
+ Arabica::XPath::NodeSet<std::string> states;
+ states.push_back(source);
+ states.push_back(tStates);
+ return findLCCA(states);
+#endif
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/interpreter/InterpreterRC.cpp.deactivated b/src/uscxml/interpreter/InterpreterRC.cpp.deactivated
deleted file mode 100644
index 9993227..0000000
--- a/src/uscxml/interpreter/InterpreterRC.cpp.deactivated
+++ /dev/null
@@ -1,1292 +0,0 @@
-/**
- * @file
- * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
- * @copyright Simplified BSD
- *
- * @cond
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the FreeBSD license as published by the FreeBSD
- * project.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * You should have received a copy of the FreeBSD license along with this
- * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
- * @endcond
- */
-
-#include "InterpreterRC.h"
-
-#include "uscxml/Factory.h"
-#include "uscxml/concurrency/DelayedEventQueue.h"
-
-#include <glog/logging.h>
-#include "uscxml/UUID.h"
-#include "uscxml/DOMUtils.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()
- */
-InterpreterState InterpreterRC::interpret() {
- try {
- tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
- if (!_isInitialized)
- init();
-
- // dump();
-
- // just make sure we have a session id
- assert(_sessionId.length() > 0);
-
- setupIOProcessors();
-
- 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 (!_dataModel) {
- Event e;
- e.data.compound["cause"] = Data("Cannot instantiate datamodel", Data::VERBATIM);
- throw e;
- }
- } else {
- _dataModel = _factory->createDataModel("null", this);
- }
- if(datamodelName.length() > 0 && !_dataModel) {
- LOG(ERROR) << "No datamodel for " << datamodelName << " registered";
- }
-
- if (_dataModel) {
- _dataModel.assign("_x.args", _cmdLineOptions);
- }
-
- _binding = (HAS_ATTR(_scxml, "binding") && 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("//" + _nsInfo.xpathPrefix + "data", _scxml).asNodeSet();
- for (unsigned int i = 0; i < dataElems.size(); i++) {
- // do not process data elements of nested documents from invokers
- if (!getAncestorElement(dataElems[i], _nsInfo.xmlNSPrefix + "invoke"))
- if (dataElems[i].getNodeType() == Node_base::ELEMENT_NODE) {
- initializeData(Element<std::string>(dataElems[i]));
- }
- }
- } else if(_dataModel) {
- // initialize current data elements
- NodeSet<std::string> topDataElems = filterChildElements(_nsInfo.xmlNSPrefix + "data", filterChildElements(_nsInfo.xmlNSPrefix + "datamodel", _scxml));
- for (unsigned int i = 0; i < topDataElems.size(); i++) {
- if (topDataElems[i].getNodeType() == Node_base::ELEMENT_NODE)
- initializeData(Element<std::string>(topDataElems[i]));
- }
- }
-
- // executeGlobalScriptElements
- NodeSet<std::string> globalScriptElems = filterChildElements(_nsInfo.xmlNSPrefix + "script", _scxml);
- for (unsigned int i = 0; i < globalScriptElems.size(); i++) {
- if (_dataModel) {
- executeContent(Element<std::string>(globalScriptElems[i]));
- }
- }
-
- NodeSet<std::string> initialTransitions;
-
- if (_startConfiguration.size() > 0) {
- // we emulate entering a given configuration by creating a pseudo deep history
- Element<std::string> initHistory = _document.createElementNS(_nsInfo.nsURL, "history");
- _nsInfo.setPrefix(initHistory);
-
- initHistory.setAttribute("id", UUID::getUUID());
- initHistory.setAttribute("type", "deep");
- _scxml.insertBefore(initHistory, _scxml.getFirstChild());
-
- std::string histId = ATTR(initHistory, "id");
- NodeSet<std::string> histStates;
- for (std::list<std::string>::const_iterator stateIter = _startConfiguration.begin(); stateIter != _startConfiguration.end(); stateIter++) {
- histStates.push_back(getState(*stateIter));
- }
- _historyValue[histId] = histStates;
-
- Element<std::string> initialElem = _document.createElementNS(_nsInfo.nsURL, "initial");
- _nsInfo.setPrefix(initialElem);
-
- initialElem.setAttribute("generated", "true");
- Element<std::string> transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition");
- _nsInfo.setPrefix(transitionElem);
-
- transitionElem.setAttribute("target", histId);
- initialElem.appendChild(transitionElem);
- _scxml.appendChild(initialElem);
- initialTransitions.push_back(transitionElem);
-
- } else {
- // try to get initial transition from initial element
- initialTransitions = _xpath.evaluate("/" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", _scxml).asNodeSet();
- if (initialTransitions.size() == 0) {
- Arabica::XPath::NodeSet<std::string> initialStates;
- // fetch per draft
- initialStates = getInitialStates();
- assert(initialStates.size() > 0);
- for (int i = 0; i < initialStates.size(); i++) {
- Element<std::string> initialElem = _document.createElementNS(_nsInfo.nsURL, "initial");
- _nsInfo.setPrefix(initialElem);
-
- initialElem.setAttribute("generated", "true");
- Element<std::string> transitionElem = _document.createElementNS(_nsInfo.nsURL, "transition");
- _nsInfo.setPrefix(transitionElem);
-
- transitionElem.setAttribute("target", ATTR_CAST(initialStates[i], "id"));
- initialElem.appendChild(transitionElem);
- _scxml.appendChild(initialElem);
- initialTransitions.push_back(transitionElem);
- }
- }
- }
-
- assert(initialTransitions.size() > 0);
-
- enterStates(initialTransitions);
- // _mutex.unlock();
-
- // assert(hasLegalConfiguration());
- mainEventLoop();
- } catch (boost::bad_weak_ptr e) {
- LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl;
- }
- // set datamodel to null from this thread
- if(_dataModel)
- _dataModel = DataModel();
-
- return _state;
-}
-
-/**
-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 InterpreterRC::mainEventLoop() {
-
- 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) {
-
- enabledTransitions = selectEventlessTransitions();
- if (enabledTransitions.size() == 0) {
- if (_internalQueue.size() == 0) {
- _stable = true;
- } else {
- _currEvent = _internalQueue.front();
- _internalQueue.pop_front();
-
- USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent)
-
- if (_dataModel)
- _dataModel.setEvent(_currEvent);
- enabledTransitions = selectTransitions(_currEvent.name);
- }
- }
- if (!enabledTransitions.empty()) {
- // test 403b
- enabledTransitions.to_document_order();
- microstep(enabledTransitions);
- }
- }
-
- if (!_running)
- goto EXIT_INTERPRETER;
-
- for (unsigned int i = 0; i < _statesToInvoke.size(); i++) {
- NodeSet<std::string> invokes = filterChildElements(_nsInfo.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
- if (!hasLegalConfiguration()) {
- std::cout << "Illegal configuration!" << std::endl;
- for (unsigned int j = 0; j < _configuration.size(); j++) {
- std::cout << ATTR_CAST(_configuration[j], "id") << " ";
- }
- std::cout << std::endl;
- }
- assert(hasLegalConfiguration());
-
- // if (!_sendQueue || _sendQueue->isEmpty()) {
-
- USCXML_MONITOR_CALLBACK(onStableConfiguration)
-
- // }
-
- _mutex.unlock();
- // whenever we have a stable configuration, run the mainThread hooks with 200fps
- while(_externalQueue.isEmpty() && _thread == NULL) {
- runOnMainThread(200);
- }
- _mutex.lock();
-
- // 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
-
- while(_externalQueue.isEmpty()) {
- _condVar.wait(_mutex);
- }
- _currEvent = _externalQueue.pop();
-#if 0
- std::cout << "Received externalEvent event " << _currEvent.name << std::endl;
-#endif
- _currEvent.eventType = Event::EXTERNAL; // make sure it is set to external
- if (!_running)
- goto EXIT_INTERPRETER;
-
- USCXML_MONITOR_CALLBACK2(beforeProcessingEvent, _currEvent)
-
- if (_dataModel && 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 << _currEvent;
- }
- }
- for (unsigned int i = 0; i < _configuration.size(); i++) {
- NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _configuration[i]);
- for (unsigned int j = 0; j < invokes.size(); j++) {
- Element<std::string> invokeElem = (Element<std::string>)invokes[j];
- std::string invokeId;
- if (HAS_ATTR(invokeElem, "id")) {
- invokeId = ATTR(invokeElem, "id");
- } else {
- if (HAS_ATTR(invokeElem, "idlocation") && _dataModel) {
- try {
- invokeId = _dataModel.evalAsString(ATTR(invokeElem, "idlocation"));
- } catch(Event e) {
- LOG(ERROR) << "Syntax error while assigning idlocation from invoke:" << std::endl << e << std::endl;
- }
- }
- }
- std::string autoForward = invokeElem.getAttribute("autoforward");
- if (iequals(invokeId, _currEvent.invokeid)) {
-
- Arabica::XPath::NodeSet<std::string> finalizes = filterChildElements(_nsInfo.xmlNSPrefix + "finalize", invokeElem);
- for (int k = 0; k < finalizes.size(); k++) {
- Element<std::string> finalizeElem = Element<std::string>(finalizes[k]);
- executeContent(finalizeElem);
- }
-
- }
- if (iequals(autoForward, "true")) {
- try {
- // do not autoforward to invokers that send to #_parent from the SCXML IO Processor!
- // Yes do so, see test229!
- // if (!boost::equals(_currEvent.getOriginType(), "http://www.w3.org/TR/scxml/#SCXMLEventProcessor"))
- _invokers[invokeId].send(_currEvent);
- } catch(...) {
- LOG(ERROR) << "Exception caught while sending event to invoker " << invokeId;
- }
- }
- }
- }
- enabledTransitions = selectTransitions(_currEvent.name);
- if (!enabledTransitions.empty()) {
- // test 403b
- enabledTransitions.to_document_order();
- microstep(enabledTransitions);
- }
- }
-
-EXIT_INTERPRETER:
- USCXML_MONITOR_CALLBACK(beforeCompletion)
-
- exitInterpreter();
- if (_sendQueue) {
- std::map<std::string, std::pair<InterpreterImpl*, SendRequest> >::iterator sendIter = _sendIds.begin();
- while(sendIter != _sendIds.end()) {
- _sendQueue->cancelEvent(sendIter->first);
- sendIter++;
- }
- }
-
- USCXML_MONITOR_CALLBACK(afterCompletion)
-
-}
-
-/**
-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 InterpreterRC::exitInterpreter() {
- NodeSet<std::string> statesToExit = _configuration;
- statesToExit.forward(false);
- statesToExit.sort();
-
- for (int i = 0; i < statesToExit.size(); i++) {
- Arabica::XPath::NodeSet<std::string> onExitElems = filterChildElements(_nsInfo.xmlNSPrefix + "onexit", statesToExit[i]);
- for (int j = 0; j < onExitElems.size(); j++) {
- executeContent(Element<std::string>(onExitElems[j]));
- }
- Arabica::XPath::NodeSet<std::string> invokeElems = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]);
- for (int j = 0; j < invokeElems.size(); j++) {
- cancelInvoke(invokeElems[j]);
- }
- Element<std::string> stateElem(statesToExit[i]);
- if (isFinal(stateElem) && parentIsScxmlState(stateElem)) {
- returnDoneEvent(stateElem);
- }
- }
- _configuration = NodeSet<std::string>();
-}
-
-/**
-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 = removeConflictingTransitions(enabledTransitions)
- return enabledTransitions
- */
-Arabica::XPath::NodeSet<std::string> InterpreterRC::selectEventlessTransitions() {
- Arabica::XPath::NodeSet<std::string> enabledTransitions;
-
- NodeSet<std::string> atomicStates;
- for (unsigned int i = 0; i < _configuration.size(); i++) {
- if (isAtomic(Element<std::string>(_configuration[i])))
- atomicStates.push_back(_configuration[i]);
- }
- atomicStates.to_document_order();
-
- for (unsigned int i = 0; i < atomicStates.size(); i++) {
- const Node<std::string>& state = atomicStates[i];
- NodeSet<std::string> withAncestors;
- withAncestors.push_back(state);
- withAncestors.push_back(getProperAncestors(state, Node<std::string>()));
- for (unsigned int j = 0; j < withAncestors.size(); j++) {
- const Node<std::string>& ancestor = withAncestors[i];
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", ancestor);
- for (unsigned int k = 0; k < transitions.size(); k++) {
- Element<std::string> transElem(transitions[k]);
- if (!HAS_ATTR(transElem, "event") && hasConditionMatch(transElem)) {
- enabledTransitions.push_back(transitions[k]);
- goto BREAK_LOOP;
- }
- }
- }
-BREAK_LOOP:
- ;
- }
-
- enabledTransitions = removeConflictingTransitions(enabledTransitions);
- return enabledTransitions;
-}
-
-/**
-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> InterpreterRC::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(Element<std::string>(_configuration[i])))
- atomicStates.push_back(_configuration[i]);
- }
- atomicStates.to_document_order();
-
-#if 0
- std::cout << "selectTransitions for " << event << "========" << std::endl;
-#endif
- for (unsigned int i = 0; i < atomicStates.size(); i++) {
- const Node<std::string>& state = atomicStates[i];
-#if 0
- std::cout << " == from " << ATTR(state, "id") << std::endl;
-#endif
-
- NodeSet<std::string> withAncestors;
- withAncestors.push_back(state);
- withAncestors.push_back(getProperAncestors(state, Node<std::string>()));
- for (unsigned int j = 0; j < withAncestors.size(); j++) {
- const Node<std::string>& ancestor = withAncestors[j];
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", ancestor);
- for (unsigned int k = 0; k < transitions.size(); k++) {
- if (isEnabledTransition(Element<std::string>(transitions[k]), event)) {
- enabledTransitions.push_back(transitions[k]);
- goto BREAK_LOOP;
- }
- }
- }
-BREAK_LOOP:
- ;
- }
-
- enabledTransitions = removeConflictingTransitions(enabledTransitions);
-
-#if 0
- std::cout << "enabledTransitions ========" << std::endl;
- for (unsigned int k = 0; k < enabledTransitions.size(); k++) {
- std::cout << enabledTransitions[k];
- }
- std::cout << std::endl;
- std::cout << "======== enabledTransitions" << std::endl;
-#endif
- return enabledTransitions;
-}
-
-bool InterpreterRC::isEnabledTransition(const Element<std::string>& transition, const std::string& event) {
- std::string eventName;
- if (HAS_ATTR(transition, "event")) {
- eventName = ATTR(transition, "event");
- } else if(HAS_ATTR(transition, "eventexpr")) {
- if (_dataModel) {
- eventName = _dataModel.evalAsString(ATTR(transition, "eventexpr"));
- } else {
- LOG(ERROR) << "Transition has eventexpr attribute with no datamodel defined";
- return false;
- }
- } else {
- return false;
- }
-
- std::list<std::string> eventNames = tokenizeIdRefs(eventName);
- std::list<std::string>::iterator eventIter = eventNames.begin();
- while(eventIter != eventNames.end()) {
- if(nameMatch(*eventIter, event) && hasConditionMatch(transition)) {
- return true;
- }
- eventIter++;
- }
- return false;
-}
-
-
-/**
-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 isDescendant(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> InterpreterRC::removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- Arabica::XPath::NodeSet<std::string> filteredTransitions;
-
- for (unsigned int i = 0; i < enabledTransitions.size(); i++) {
- Element<std::string> t1(enabledTransitions[i]);
- bool t1Preempted = false;
- Arabica::XPath::NodeSet<std::string> transitionsToRemove;
-
- for (unsigned int j = 0; j < filteredTransitions.size(); j++) {
- Element<std::string> t2(enabledTransitions[j]);
- if (hasIntersection(computeExitSet(t1), computeExitSet(t2))) {
- if (isDescendant(getSourceState(t1), getSourceState(t2))) {
- transitionsToRemove.push_back(t2);
- } else {
- t1Preempted = true;
- break;
- }
- }
- }
-
- if (!t1Preempted) {
- // remove transitionsToRemove from filteredTransitions
- std::list<Node<std::string> > tmp;
- for (int i = 0; i < filteredTransitions.size(); i++) {
- if (!isMember(filteredTransitions[i], transitionsToRemove)) {
- tmp.push_back(filteredTransitions[i]);
- }
- }
- filteredTransitions = NodeSet<std::string>();
- filteredTransitions.insert(_statesToInvoke.end(), tmp.begin(), tmp.end());
-
- filteredTransitions.push_back(t1);
- }
- }
- return filteredTransitions;
-}
-
-bool InterpreterRC::hasIntersection(const Arabica::XPath::NodeSet<std::string>& nodeSet1, const Arabica::XPath::NodeSet<std::string>& nodeSet2) {
- for (unsigned int i = 0; i < nodeSet1.size(); i++) {
- for (unsigned int j = 0; j < nodeSet2.size(); j++) {
- if (nodeSet1[i] == nodeSet2[j])
- return true;
- }
- }
- return false;
-}
-
-/**
-procedure microstep(enabledTransitions):
- exitStates(enabledTransitions)
- executeTransitionContent(enabledTransitions)
- enterStates(enabledTransitions)
- */
-void InterpreterRC::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
-
- USCXML_MONITOR_CALLBACK(beforeMicroStep)
-
- exitStates(enabledTransitions);
-
- for (int i = 0; i < enabledTransitions.size(); i++) {
- Element<std::string> transition(enabledTransitions[i]);
-
- USCXML_MONITOR_CALLBACK3(beforeTakingTransition, transition, (i + 1 < enabledTransitions.size()))
-
- executeContent(transition);
-
- USCXML_MONITOR_CALLBACK3(afterTakingTransition, transition, (i + 1 < enabledTransitions.size()))
- }
-
- enterStates(enabledTransitions);
-
- USCXML_MONITOR_CALLBACK(afterMicroStep)
-}
-
-
-/**
-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 InterpreterRC::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- NodeSet<std::string> statesToExit = computeExitSet(enabledTransitions);
-
- // remove statesToExit from _statesToInvoke
- std::list<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.forward(false);
- statesToExit.sort();
-
- for (int i = 0; i < statesToExit.size(); i++) {
- NodeSet<std::string> histories = filterChildElements(_nsInfo.xmlNSPrefix + "history", statesToExit[i]);
- for (int j = 0; j < histories.size(); j++) {
- Element<std::string> historyElem = (Element<std::string>)histories[j];
- std::string historyType = (historyElem.hasAttribute("type") ? historyElem.getAttribute("type") : "shallow");
- NodeSet<std::string> historyNodes;
- for (int k = 0; k < _configuration.size(); k++) {
- if (iequals(historyType, "deep")) {
- if (isAtomic(Element<std::string>(_configuration[k])) && isDescendant(_configuration[k], statesToExit[i]))
- historyNodes.push_back(_configuration[k]);
- } else {
- if (_configuration[k].getParentNode() == statesToExit[i])
- historyNodes.push_back(_configuration[k]);
- }
- }
- _historyValue[historyElem.getAttribute("id")] = historyNodes;
- }
- }
-
- for (int i = 0; i < statesToExit.size(); i++) {
- USCXML_MONITOR_CALLBACK3(beforeExitingState, Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size()))
-
- NodeSet<std::string> onExits = filterChildElements(_nsInfo.xmlNSPrefix + "onExit", statesToExit[i]);
- for (int j = 0; j < onExits.size(); j++) {
- Element<std::string> onExitElem = (Element<std::string>)onExits[j];
- executeContent(onExitElem);
- }
-
- USCXML_MONITOR_CALLBACK3(afterExitingState, Element<std::string>(statesToExit[i]), (i + 1 < statesToExit.size()))
-
- NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", statesToExit[i]);
- for (int j = 0; j < invokes.size(); j++) {
- Element<std::string> invokeElem = (Element<std::string>)invokes[j];
- cancelInvoke(invokeElem);
- }
-
- // remove statesToExit[i] from _configuration - test409
- tmp.clear();
- for (int j = 0; j < _configuration.size(); j++) {
- if (_configuration[j] != statesToExit[i]) {
- tmp.push_back(_configuration[j]);
- }
- }
- _configuration = NodeSet<std::string>();
- _configuration.insert(_configuration.end(), tmp.begin(), tmp.end());
- }
-}
-
-
-/**
-function computeExitSet(transitions)
- statesToExit = new OrderedSet
- for t in transitions:
- if(getTargetSet(t.target)):
- domain = getTransitionDomain(t)
- for s in configuration:
- if isDescendant(s,domain):
- statesToExit.add(s)
- return statesToExit
- */
-Arabica::XPath::NodeSet<std::string> InterpreterRC::computeExitSet(const Arabica::XPath::NodeSet<std::string>& transitions) {
- NodeSet<std::string> statesToExit;
- for (unsigned int i = 0; i < transitions.size(); i++) {
- Element<std::string> t(transitions[i]);
- if (isTargetless(t))
- continue;
- Arabica::DOM::Node<std::string> domain = getTransitionDomain(t);
- if (!domain)
- continue;
- for (unsigned int j = 0; j < _configuration.size(); j++) {
- const Node<std::string>& s = _configuration[j];
- if (isDescendant(s, domain)) {
- statesToExit.push_back(s);
- }
- }
- }
-#if 0
- std::cout << "computeExitSet: ";
- for (int i = 0; i < statesToExit.size(); i++) {
- std::cout << ATTR(statesToExit[i], "id") << " ";
- }
- std::cout << std::endl;
-#endif
- return statesToExit;
-}
-
-Arabica::XPath::NodeSet<std::string> InterpreterRC::computeExitSet(const Arabica::DOM::Node<std::string>& transition) {
- Arabica::XPath::NodeSet<std::string> transitions;
- transitions.push_back(transition);
- return computeExitSet(transitions);
-}
-
-
-/**
-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 InterpreterRC::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
- NodeSet<std::string> statesToEnter;
- NodeSet<std::string> statesForDefaultEntry;
- // initialize the temporary table for default content in history states
- std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent;
-
- computeEntrySet(enabledTransitions, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- statesToEnter.to_document_order();
-
- for (int i = 0; i < statesToEnter.size(); i++) {
- Element<std::string> s = (Element<std::string>)statesToEnter[i];
-
- USCXML_MONITOR_CALLBACK3(beforeEnteringState, s, i + 1 < statesToEnter.size())
-
- _configuration.push_back(s);
- _statesToInvoke.push_back(s);
-
- // if (_binding == LATE && stateElem.getAttribute("isFirstEntry").size() > 0) {
- if (_binding == LATE && !isMember(s, _alreadyEntered)) {
- NodeSet<std::string> dataModelElems = filterChildElements(_nsInfo.xmlNSPrefix + "datamodel", s);
- if(dataModelElems.size() > 0 && _dataModel) {
- Arabica::XPath::NodeSet<std::string> dataElems = filterChildElements(_nsInfo.xmlNSPrefix + "data", dataModelElems[0]);
- for (int j = 0; j < dataElems.size(); j++) {
- if (dataElems[j].getNodeType() == Node_base::ELEMENT_NODE)
- initializeData(Element<std::string>(dataElems[j]));
- }
- }
- _alreadyEntered.push_back(s);
- // stateElem.setAttribute("isFirstEntry", "");
- }
- // execute onentry executable content
- NodeSet<std::string> onEntryElems = filterChildElements(_nsInfo.xmlNSPrefix + "onEntry", s);
- executeContent(onEntryElems, false);
-
- USCXML_MONITOR_CALLBACK3(afterEnteringState, s, i + 1 < statesToEnter.size())
-
- if (isMember(s, statesForDefaultEntry)) {
- // execute initial transition content for compound states
- Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _nsInfo.xpathPrefix + "initial/" + _nsInfo.xpathPrefix + "transition", s).asNodeSet();
- executeContent(transitions);
- }
- if (defaultHistoryContent.find(ATTR(s, "id")) != defaultHistoryContent.end()) {
- executeContent(Element<std::string>(defaultHistoryContent[ATTR(s, "id")]));
- }
-
- /**
- 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))
- */
- //std::cout << _name << ": " << s << std::endl;
-
- if (isFinal(s)) {
- internalDoneSend(s);
- if (parentIsScxmlState(s)) {
- _running = false;
- _topLevelFinalReached = true;
- } else {
- Element<std::string> parent = (Element<std::string>)s.getParentNode();
- Element<std::string> grandParent = (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);
- }
- }
- }
- }
- }
-}
-
-
-/**
-procedure computeEntrySet(transitions, statesToEnter, statesForDefaultEntry)
- for t in transitions:
- statesToEnter.union(getTargetStates(t.target))
- for s in statesToEnter:
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry)
- for t in transitions:
- ancestor = getTransitionDomain(t)
- for s in getTargetStates(t.target)):
- addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry)
- */
-void InterpreterRC::computeEntrySet(const Arabica::XPath::NodeSet<std::string>& transitions,
- NodeSet<std::string>& statesToEnter,
- NodeSet<std::string>& statesForDefaultEntry,
- std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent) {
- for (int i = 0; i < transitions.size(); i++) {
- Element<std::string> t(transitions[i]);
-
- NodeSet<std::string> targets = getTargetStates(t);
- for (int j = 0; j < targets.size(); j++) {
- if (!isMember(targets[j], statesToEnter)) {
- statesToEnter.push_back(targets[j]);
- }
- }
- }
-
-#if 0
- std::cout << "before addDescendantStatesToEnter: ";
- for (int i = 0; i < statesToEnter.size(); i++) {
- std::cout << ATTR(statesToEnter[i], "id") << " ";
- }
- std::cout << std::endl;
-#endif
-
- NodeSet<std::string> tmp = statesToEnter;
- for (int i = 0; i < tmp.size(); i++) {
- assert(tmp[i]);
- addDescendantStatesToEnter(tmp[i],statesToEnter,statesForDefaultEntry, defaultHistoryContent);
- }
-
-#if 0
- std::cout << "after addDescendantStatesToEnter: ";
- for (int i = 0; i < statesToEnter.size(); i++) {
- std::cout << ATTR(statesToEnter[i], "id") << " ";
- }
- std::cout << std::endl;
-#endif
-
- for (int i = 0; i < transitions.size(); i++) {
- Element<std::string> t = (Element<std::string>)transitions[i];
- Node<std::string> ancestor = getTransitionDomain(t);
- NodeSet<std::string> targets = getTargetStates(t);
- for (int j = 0; j < targets.size(); j++) {
- const Node<std::string>& s = targets[j];
- addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
- }
-
-#if 0
- std::cout << "after addAncestorStatesToEnter: ";
- for (int i = 0; i < statesToEnter.size(); i++) {
- std::cout << ATTR(statesToEnter[i], "id") << " ";
- }
- std::cout << std::endl;
-#endif
-}
-
-void InterpreterRC::computeEntrySet(const Arabica::DOM::Node<std::string>& transition,
- NodeSet<std::string>& statesToEnter,
- NodeSet<std::string>& statesForDefaultEntry,
- std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent) {
- Arabica::XPath::NodeSet<std::string> transitions;
- transitions.push_back(transition);
- computeEntrySet(transitions, statesToEnter, statesForDefaultEntry, defaultHistoryContent);
-}
-
-
-/**
-procedure addDescendantStatesToEnter(state,statesToEnter,statesForDefaultEntry):
- if isHistoryState(state):
- if historyValue[state.id]:
- for s in historyValue[state.id]:
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry)
- addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry)
- else:
- for t in state.transition:
- for s in getTargetStates(t.target):
- addDescendantStatesToEnter(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):
- addDescendantStatesToEnter(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)):
- addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry)
- */
-void InterpreterRC::addDescendantStatesToEnter(const Arabica::DOM::Node<std::string>& state,
- Arabica::XPath::NodeSet<std::string>& statesToEnter,
- Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
- std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent) {
- if (isHistory(Element<std::string>(state))) {
- std::string stateId = ATTR(state, "id");
- if (_historyValue.find(stateId) != _historyValue.end()) {
- const Arabica::XPath::NodeSet<std::string>& historyValue = _historyValue[stateId];
- for (int i = 0; i < historyValue.size(); i++) {
- const Node<std::string>& s = historyValue[i];
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
- addAncestorStatesToEnter(s, getParentState(s), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
- } else {
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", state);
- if (transitions.size() > 0) {
- defaultHistoryContent[ATTR(state, "id")] = transitions[0];
- }
- if (transitions.size() > 1) {
- LOG(ERROR) << "Only one transition allowed in history";
- }
- for (int i = 0; i < transitions.size(); i++) {
- NodeSet<std::string> targets = getTargetStates(Element<std::string>(transitions[i]));
- for (int j = 0; j < targets.size(); j++) {
- const Node<std::string>& s = targets[i];
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
- addAncestorStatesToEnter(s, getParentState(s), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
- }
- }
- } else {
- if (!isMember(state, statesToEnter)) // adding an existing element invalidates old reference
- statesToEnter.push_back(state);
-
- if (isCompound(Element<std::string>(state))) {
- statesForDefaultEntry.push_back(state);
- NodeSet<std::string> targets = getInitialStates(Element<std::string>(state));
- for (int i = 0; i < targets.size(); i++) {
- const Node<std::string>& s = targets[i];
- addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
- addAncestorStatesToEnter(s, getParentState(s), statesToEnter, statesForDefaultEntry, defaultHistoryContent);
- }
- } else if(isParallel(Element<std::string>(state))) {
- // if state is a parallel state, recursively call addStatesToEnter on any of its child
- // states that don't already have a descendant on statesToEnter.
- NodeSet<std::string> childStates = getChildStates(state);
- for (int i = 0; i < childStates.size(); i++) {
- const Node<std::string>& child = childStates[i];
- for (int j = 0; j < statesToEnter.size(); j++) {
- const Node<std::string>& s = statesToEnter[j];
- if (isDescendant(s, child)) {
- goto BREAK_LOOP;
- }
- }
- addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
-BREAK_LOOP:
- ;
- }
- }
- }
-}
-
-
-/**
-procedure addAncestorStatesToEnter(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)):
- addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry)
- */
-void InterpreterRC::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,
- std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent) {
- NodeSet<std::string> ancestors = getProperAncestors(state,ancestor);
- for (int i = 0; i < ancestors.size(); i++) {
- const Node<std::string>& anc = ancestors[i];
- statesToEnter.push_back(anc);
- if (isParallel(Element<std::string>(anc))) {
- NodeSet<std::string> childStates = getChildStates(anc);
- for (int j = 0; j < childStates.size(); j++) {
- const Node<std::string>& child = childStates[j];
- for (int k = 0; k < statesToEnter.size(); k++) {
- const Node<std::string>& s = statesToEnter[k];
- if (isDescendant(s, child)) {
- goto BREAK_LOOP;
- }
- }
- addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent);
-BREAK_LOOP:
- ;
- }
- }
- }
-}
-
-/**
-function isInFinalState(s):
- if isCompoundState(s):
- return getChildStates(s).some(lambda s: isFinalState(s) and configuration.isMember(s))
- elif isParallelState(s):
- return getChildStates(s).every(isInFinalState)
- else:
- return false
-*/
-bool InterpreterRC::isInFinalState(const Arabica::DOM::Node<std::string>& state) {
- if (isCompound(Element<std::string>(state))) {
- Arabica::XPath::NodeSet<std::string> childs = getChildStates(state);
- for (int i = 0; i < childs.size(); i++) {
- if (isFinal(Element<std::string>(childs[i])) && isMember(childs[i], _configuration))
- return true;
- }
- } else if (isParallel(Element<std::string>(state))) {
- Arabica::XPath::NodeSet<std::string> childs = getChildStates(state);
- for (int i = 0; i < childs.size(); i++) {
- if (!isInFinalState(childs[i]))
- return false;
- }
- return true;
- }
- return false;
-}
-
-
-/**
-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> InterpreterRC::getTransitionDomain(const Arabica::DOM::Element<std::string>& transition) {
- NodeSet<std::string> tStates = getTargetStates(transition);
- Node<std::string> source = getSourceState(transition);
-
-#if 0
- std::cout << "getTransitionDomain: " << std::endl << transition << std::endl;
-#endif
-
- if (tStates.size() == 0) {
- return Arabica::DOM::Node<std::string>(); // null
- }
- std::string transitionType = (HAS_ATTR(transition, "type") ? ATTR(transition, "type") : "external");
-
- if (iequals(transitionType, "internal") && isCompound(Element<std::string>(source))) {
- for (int i = 0; i < tStates.size(); i++) {
- const Node<std::string>& s = tStates[i];
- if (!isDescendant(s, source))
- goto BREAK_LOOP;
- }
- return source;
- }
-BREAK_LOOP:
- ;
- Arabica::XPath::NodeSet<std::string> states;
- states.push_back(source);
- states.push_back(tStates);
- return findLCCA(states);
-}
-
-
-/**
-function findLCCA(stateList):
- for anc in getProperAncestors(stateList.head(),null).filter(isCompoundStateOrScxmlElement):
- if stateList.tail().every(lambda s: isDescendant(s,anc)):
- return anc
- */
-Arabica::DOM::Node<std::string> InterpreterRC::findLCCA(const Arabica::XPath::NodeSet<std::string>& states) {
- Arabica::XPath::NodeSet<std::string> ancestors = getProperAncestors(states[0], Arabica::DOM::Node<std::string>());
- // ancestors.push_back(states[0]); // state[0] may already be the ancestor - bug in W3C spec?
- Arabica::DOM::Node<std::string> ancestor;
- for (int i = 0; i < ancestors.size(); i++) {
- if (!isCompound(Element<std::string>(ancestors[i])))
- continue;
- for (int j = 0; j < states.size(); j++) {
- if (!isDescendant(states[j], ancestors[i]) && (states[j] != ancestors[i]))
- goto NEXT_ANCESTOR;
- }
- ancestor = ancestors[i];
- break;
-NEXT_ANCESTOR:
- ;
- }
-
- // take uppermost root as ancestor
- if (!ancestor)
- ancestor = _scxml;
- assert(ancestor);
- return ancestor;
-}
-
-/**
- If state2 is null, returns the set of all ancestors of state1 in ancestry order
- (state1's parent followed by the parent's parent, etc. up to an including the
- <scxml> element). If state2 is non-null, returns inancestry order the set of all
- ancestors of state1, up to but not including state2. (A "proper ancestor" of a
- state is its parent, or the parent's parent, or the parent's parent's parent,
- etc.)) If state2 is state1's parent, or equal to state1, or a descendant of
- state1, this returns the empty set.
-*/
-Arabica::XPath::NodeSet<std::string> InterpreterRC::getProperAncestors(const Arabica::DOM::Node<std::string>& state1, const Arabica::DOM::Node<std::string>& state2) {
- NodeSet<std::string> ancestors;
-
- if (!state1 || !isState(Element<std::string>(state1)))
- return ancestors;
-
- if (!state2) {
- /**
- If state2 is null, returns the set of all ancestors of state1 in ancestry
- order (state1's parent followed by the parent's parent, etc. up to an
- including the <scxml> element).
- */
- Arabica::DOM::Node<std::string> parent = state1.getParentNode();
- while(parent && isState(Element<std::string>(parent))) {
- ancestors.push_back(parent);
- parent = parent.getParentNode();
- }
- return ancestors;
- }
-
- /**
- If state2 is state1's parent, or equal to state1, or a descendant of
- state1, this returns the empty set
- */
- if (state1.getParentNode() == state2 || state1 == state2 || isDescendant(state2, state1)) {
- return ancestors;
- }
-
- /**
- If state2 is non-null, returns in ancestry order the set of all ancestors
- of state1, up to but not including state2.
- */
- Arabica::DOM::Node<std::string> parent = state1.getParentNode();
- while(parent && isState(Element<std::string>(parent)) && parent != state2) {
- ancestors.push_back(parent);
- parent = parent.getParentNode();
- }
- return ancestors;
-}
-
-NodeSet<std::string> InterpreterRC::getTargetStates(const Arabica::DOM::Element<std::string>& transition) {
- NodeSet<std::string> targetStates;
-
- std::string targetId = ((Arabica::DOM::Element<std::string>)transition).getAttribute("target");
- std::list<std::string> targetIds = InterpreterImpl::tokenizeIdRefs(ATTR(transition, "target"));
- if (targetIds.size() > 0) {
- for (std::list<std::string>::const_iterator targetIter = targetIds.begin(); targetIter != targetIds.end(); targetIter++) {
- Arabica::DOM::Node<std::string> state = getState(*targetIter);
- if (state) {
- assert(HAS_ATTR(state, "id"));
- targetStates.push_back(state);
- }
- }
- } else {
- targetStates.push_back(getSourceState(transition)); // TODO: is this till correct?
- }
- return targetStates;
-}
-
-
-#if 0
-/**
-Returns 'true' if state1 is a descendant of state2 (a child, or a child of a child, or a child of a child of a child, etc.) Otherwise returns 'false'.
-*/
-bool InterpreterRC::isDescendant(const Arabica::DOM::Node<std::string>& state1, const Arabica::DOM::Node<std::string>& state2) {
- return false;
-}
-
-/**
-Returns a list containing all <state>, <final>, and <parallel> children of state1.
-*/
-Arabica::XPath::NodeSet<std::string> InterpreterRC::getChildStates(const Arabica::DOM::Node<std::string>& state) {
- return Arabica::XPath::NodeSet<std::string>();
-}
-#endif
-
-
-} \ No newline at end of file
diff --git a/src/uscxml/interpreter/InterpreterRC.h.deactivated b/src/uscxml/interpreter/InterpreterRC.h
index 9afc2e6..3dae973 100644
--- a/src/uscxml/interpreter/InterpreterRC.h.deactivated
+++ b/src/uscxml/interpreter/InterpreterRC.h
@@ -25,19 +25,11 @@
namespace uscxml {
class InterpreterRC : public InterpreterImpl {
- InterpreterState 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);
- bool isEnabledTransition(const Arabica::DOM::Element<std::string>& transition, const std::string& event);
- bool hasIntersection(const Arabica::XPath::NodeSet<std::string>& nodeSet1, const Arabica::XPath::NodeSet<std::string>& nodeSet2);
-
void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
+ Arabica::XPath::NodeSet<std::string> removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
+
+ bool hasIntersection(const Arabica::XPath::NodeSet<std::string>& nodeSet1, const Arabica::XPath::NodeSet<std::string>& nodeSet2);
Arabica::XPath::NodeSet<std::string> computeExitSet(const Arabica::XPath::NodeSet<std::string>& transitions);
Arabica::XPath::NodeSet<std::string> computeExitSet(const Arabica::DOM::Node<std::string>& transition);
@@ -51,35 +43,20 @@ class InterpreterRC : public InterpreterImpl {
Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent);
- Arabica::XPath::NodeSet<std::string> removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
Arabica::DOM::Node<std::string> getTransitionDomain(const Arabica::DOM::Element<std::string>& transition);
+ Arabica::XPath::NodeSet<std::string> getEffectiveTargetStates(const Arabica::DOM::Element<std::string>& transition);
- void addDescendantStatesToEnter(const Arabica::DOM::Node<std::string>& state,
+ void addDescendantStatesToEnter(const Arabica::DOM::Element<std::string>& state,
Arabica::XPath::NodeSet<std::string>& statesToEnter,
Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent);
- void addAncestorStatesToEnter(const Arabica::DOM::Node<std::string>& state,
- const Arabica::DOM::Node<std::string>& ancestor,
+ void addAncestorStatesToEnter(const Arabica::DOM::Element<std::string>& state,
+ const Arabica::DOM::Element<std::string>& ancestor,
Arabica::XPath::NodeSet<std::string>& statesToEnter,
Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry,
std::map<std::string, Arabica::DOM::Node<std::string> > defaultHistoryContent);
- bool isInFinalState(const Arabica::DOM::Node<std::string>& state);
- 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);
-
- Arabica::XPath::NodeSet<std::string> getTargetStates(const Arabica::DOM::Element<std::string>& transition);
-
-#if 0
- bool isDescendant(const Arabica::DOM::Node<std::string>& state1, const Arabica::DOM::Node<std::string>& state2);
- Arabica::XPath::NodeSet<std::string> getChildStates(const Arabica::DOM::Node<std::string>& state);
-#endif
-
- bool _running;
-
};
}
diff --git a/src/uscxml/plugins/EventHandler.h b/src/uscxml/plugins/EventHandler.h
index 2b41ae5..8ac31b1 100644
--- a/src/uscxml/plugins/EventHandler.h
+++ b/src/uscxml/plugins/EventHandler.h
@@ -65,16 +65,17 @@ public:
virtual void send(const SendRequest& req) = 0;
virtual void runOnMainThread() {};
- void returnEvent(Event& event, bool external = false);
void returnErrorExecution(const std::string&);
void returnErrorCommunication(const std::string&);
+ void returnEvent(Event& event, bool internal = false);
protected:
InterpreterImpl* _interpreter;
Arabica::DOM::Element<std::string> _element;
std::string _invokeId;
std::string _type;
-
+
+
};
class USCXML_API EventHandler {
diff --git a/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp b/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp
index cc06b52..f8b4904 100644
--- a/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp
+++ b/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp
@@ -98,7 +98,7 @@ void HeartbeatInvoker::invoke(const InvokeRequest& req) {
void HeartbeatInvoker::dispatch(void* instance, std::string name) {
HeartbeatInvoker* invoker = (HeartbeatInvoker*)instance;
- invoker->returnEvent(invoker->_event, true);
+ invoker->returnEvent(invoker->_event);
}
HeartbeatDispatcher* HeartbeatDispatcher::_instance = NULL;
diff --git a/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp b/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp
index 861e922..e2b32df 100644
--- a/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp
+++ b/src/uscxml/plugins/invoker/xhtml/XHTMLInvoker.cpp
@@ -101,7 +101,7 @@ bool XHTMLInvoker::httpRecvRequest(const HTTPServer::Request& req) {
HTTPServer::Reply reply(req);
HTTPServer::reply(reply);
- returnEvent(ev, true);
+ returnEvent(ev);
return true;
}
}
diff --git a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp
index 845e142..cf7c8e7 100644
--- a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp
+++ b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp
@@ -157,7 +157,7 @@ bool BasicHTTPIOProcessor::httpRecvRequest(const HTTPServer::Request& req) {
if (reqEvent.name.length() == 0)
reqEvent.name = "http." + req.data.compound.at("type").atom;
- returnEvent(reqEvent, true);
+ returnEvent(reqEvent);
evhttp_send_reply(req.evhttpReq, 200, "OK", NULL);
return true;
}
diff --git a/src/uscxml/transform/ChartToFSM.cpp b/src/uscxml/transform/ChartToFSM.cpp
index c31853c..1971ae2 100644
--- a/src/uscxml/transform/ChartToFSM.cpp
+++ b/src/uscxml/transform/ChartToFSM.cpp
@@ -580,7 +580,7 @@ void FlatteningInterpreter::explode() {
continue;
// reduce to conflict-free subset
- transitions = filterPreempted(transitions);
+ transitions = removeConflictingTransitions(transitions);
// algorithm can never reduce to empty set
assert(transitions.size() > 0);