summaryrefslogtreecommitdiffstats
path: root/src/uscxml/interpreter/InterpreterRC.cpp
diff options
context:
space:
mode:
authorStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2014-08-26 08:54:58 (GMT)
committerStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2014-08-26 08:54:58 (GMT)
commitd90b15c96271d2afd45d649e3e22030004359d6e (patch)
treedb773868e71fd51fb05e5160e1138bec8087eac9 /src/uscxml/interpreter/InterpreterRC.cpp
parent93802c6eab1f798ccf57e863127aef6e7f9d073c (diff)
downloaduscxml-d90b15c96271d2afd45d649e3e22030004359d6e.zip
uscxml-d90b15c96271d2afd45d649e3e22030004359d6e.tar.gz
uscxml-d90b15c96271d2afd45d649e3e22030004359d6e.tar.bz2
API changes with receiveEvent
- receiveEvent will deliver to external queue now per default - switched Interpreter implementations to the new algorithm from spec
Diffstat (limited to 'src/uscxml/interpreter/InterpreterRC.cpp')
-rw-r--r--src/uscxml/interpreter/InterpreterRC.cpp579
1 files changed, 579 insertions, 0 deletions
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