diff options
author | Stefan Radomski <github@mintwerk.de> | 2016-05-12 13:12:33 (GMT) |
---|---|---|
committer | Stefan Radomski <github@mintwerk.de> | 2016-05-12 13:12:33 (GMT) |
commit | b62e7979600feee23dc7cdb61042a8fc7673122b (patch) | |
tree | f7351372f37979dd2d048e0b68a16a4cd3b2aadb /src/uscxml/debug | |
parent | 1b11b310be61e51b3ac5ebb83f7c8a33aef3d6e8 (diff) | |
download | uscxml-b62e7979600feee23dc7cdb61042a8fc7673122b.zip uscxml-b62e7979600feee23dc7cdb61042a8fc7673122b.tar.gz uscxml-b62e7979600feee23dc7cdb61042a8fc7673122b.tar.bz2 |
Major Refactoring v2.0
Diffstat (limited to 'src/uscxml/debug')
-rw-r--r-- | src/uscxml/debug/Breakpoint.cpp | 266 | ||||
-rw-r--r-- | src/uscxml/debug/Breakpoint.h | 95 | ||||
-rw-r--r-- | src/uscxml/debug/Complexity.cpp | 206 | ||||
-rw-r--r-- | src/uscxml/debug/Complexity.h | 78 | ||||
-rw-r--r-- | src/uscxml/debug/DebugSession.cpp | 378 | ||||
-rw-r--r-- | src/uscxml/debug/DebugSession.h | 99 | ||||
-rw-r--r-- | src/uscxml/debug/Debugger.cpp | 256 | ||||
-rw-r--r-- | src/uscxml/debug/Debugger.h | 105 | ||||
-rw-r--r-- | src/uscxml/debug/DebuggerServlet.cpp | 262 | ||||
-rw-r--r-- | src/uscxml/debug/DebuggerServlet.h | 106 | ||||
-rw-r--r-- | src/uscxml/debug/InterpreterIssue.cpp | 601 | ||||
-rw-r--r-- | src/uscxml/debug/InterpreterIssue.h | 8 | ||||
-rw-r--r-- | src/uscxml/debug/SCXMLDotWriter.cpp | 888 | ||||
-rw-r--r-- | src/uscxml/debug/SCXMLDotWriter.h | 201 |
14 files changed, 353 insertions, 3196 deletions
diff --git a/src/uscxml/debug/Breakpoint.cpp b/src/uscxml/debug/Breakpoint.cpp deleted file mode 100644 index d7eb2af..0000000 --- a/src/uscxml/debug/Breakpoint.cpp +++ /dev/null @@ -1,266 +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 "uscxml/debug/Breakpoint.h" -#include "uscxml/Interpreter.h" -#include "uscxml/dom/DOMUtils.h" - -namespace uscxml { - -Breakpoint::Breakpoint(const Data& data) { - enabled = true; - subject = UNDEF_SUBJECT; - when = UNDEF_WHEN; - action = UNDEF_ACTION; - - if (data.hasKey("when")) { - if (false) { - } else if (data.at("when").atom == "before") { - when = BEFORE; - } else if (data.at("when").atom == "after") { - when = AFTER; - } else if (data.at("when").atom == "on") { - when = ON; - } - } - - if (data.hasKey("action")) { - if (false) { - } else if (data.at("action").atom == "enter") { - action = ENTER; - } else if (data.at("action").atom == "exit") { - action = EXIT; - } else if (data.at("action").atom == "invoke") { - action = INVOKE; - } else if (data.at("action").atom == "cancel") { - action = UNINVOKE; - } - } - - if (data.hasKey("subject")) { - if (false) { - } else if (data.at("subject").atom == "state") { - subject = STATE; - } else if (data.at("subject").atom == "transition") { - subject = TRANSITION; - } else if (data.at("subject").atom == "stable") { - subject = STABLE; - } else if (data.at("subject").atom == "microstep") { - subject = MICROSTEP; - } else if (data.at("subject").atom == "event") { - subject = EVENT; - } else if (data.at("subject").atom == "invoker") { - subject = INVOKER; - } else if (data.at("subject").atom == "executable") { - subject = EXECUTABLE; - } - } - - if (data.hasKey("condition")) - condition = data.at("condition").atom; - - if (data.hasKey("invokeId")) - invokeId = data.at("invokeId").atom; - - if (data.hasKey("invokeType")) - invokeType = data.at("invokeType").atom; - - if (data.hasKey("eventName")) - eventName = data.at("eventName").atom; - - if (data.hasKey("executableName")) - executableName = data.at("executableName").atom; - - if (data.hasKey("executableXPath")) - executableXPath = data.at("executableXPath").atom; - - if (data.hasKey("stateId")) - stateId = data.at("stateId").atom; - - if (data.hasKey("transSourceId")) - transSourceId = data.at("transSourceId").atom; - - if (data.hasKey("transTargetId")) - transTargetId = data.at("transTargetId").atom; - -} - -Data Breakpoint::toData() const { - Data data; - - switch (subject) { - case STATE: - data.compound["subject"] = Data("state", Data::VERBATIM); - break; - case TRANSITION: - data.compound["subject"] = Data("transition", Data::VERBATIM); - break; - case STABLE: - data.compound["subject"] = Data("stable", Data::VERBATIM); - break; - case MICROSTEP: - data.compound["subject"] = Data("microstep", Data::VERBATIM); - break; - case EVENT: - data.compound["subject"] = Data("event", Data::VERBATIM); - break; - case INVOKER: - data.compound["subject"] = Data("invoker", Data::VERBATIM); - break; - case EXECUTABLE: - data.compound["subject"] = Data("executable", Data::VERBATIM); - break; - default: - break; - } - - switch (when) { - case AFTER: - data.compound["when"] = Data("after", Data::VERBATIM); - break; - case BEFORE: - data.compound["when"] = Data("before", Data::VERBATIM); - break; - case ON: - data.compound["when"] = Data("on", Data::VERBATIM); - break; - default: - break; - } - - switch (action) { - case ENTER: - data.compound["action"] = Data("enter", Data::VERBATIM); - break; - case EXIT: - data.compound["action"] = Data("exit", Data::VERBATIM); - break; - case INVOKE: - data.compound["action"] = Data("invoke", Data::VERBATIM); - break; - case UNINVOKE: - data.compound["action"] = Data("cancel", Data::VERBATIM); - break; - default: - break; - } - - if (invokeId.length() > 0) - data.compound["invokeId"] = Data(invokeId, Data::VERBATIM); - - if (invokeType.length() > 0) - data.compound["invokeType"] = Data(invokeType, Data::VERBATIM); - - if (eventName.length() > 0) - data.compound["eventName"] = Data(eventName, Data::VERBATIM); - - if (executableName.length() > 0) - data.compound["executableName"] = Data(executableName, Data::VERBATIM); - - if (executableXPath.length() > 0) { - data.compound["executableXPath"] = Data(executableXPath, Data::VERBATIM); - } - - if (element) - data.compound["xpath"] = Data(DOMUtils::xPathForNode(element, "*"), Data::VERBATIM); - - if (stateId.length() > 0) - data.compound["stateId"] = Data(stateId, Data::VERBATIM); - - if (transSourceId.length() > 0) - data.compound["transSourceId"] = Data(transSourceId, Data::VERBATIM); - - if (transTargetId.length() > 0) - data.compound["transTargetId"] = Data(transTargetId, Data::VERBATIM); - - if (condition.length() > 0) - data.compound["condition"] = Data(condition, Data::VERBATIM); - - return data; -} - -bool Breakpoint::matches(Interpreter interpreter, const Breakpoint& other) const { - // would we match the given breakpoint? - - if (subject != UNDEF_SUBJECT && - other.subject != subject) - return false; // subject does not match - - if (when != UNDEF_WHEN && - other.when != when) - return false; // time does not match - - if (action != UNDEF_ACTION && - other.action != action) - return false; // action does not match - - // when we have a qualifier it has to match - if(invokeId.length() > 0 && invokeId != other.invokeId) { - return false; - } - - if(invokeType.length() > 0 && invokeType != other.invokeType) { - return false; - } - - if(stateId.length() > 0 && stateId != other.stateId) { - return false; - } - - if(eventName.length() > 0 && !nameMatch(eventName, other.eventName)) { - return false; - } - - if(executableName.length() > 0 && executableName != other.executableName) { - return false; - } - - if(executableXPath.length()) { - Arabica::XPath::NodeSet<std::string> nodes; - try { - nodes = interpreter.getNodeSetForXPath(executableXPath); - } catch (...) { - return false; - } - return InterpreterImpl::isMember(other.element, nodes); - } - - if(transSourceId.length() > 0 && transSourceId != other.transSourceId) { - return false; - } - - if(transTargetId.length() > 0 && transTargetId != other.transTargetId) { - return false; - } - - if (condition.length() > 0) { - try { - DataModel dm = interpreter.getDataModel(); - if (!dm || !dm.evalAsBool(condition)) { - return false; - } - } catch (...) { - return false; - } - } - - return true; -} - -}
\ No newline at end of file diff --git a/src/uscxml/debug/Breakpoint.h b/src/uscxml/debug/Breakpoint.h deleted file mode 100644 index d7df03d..0000000 --- a/src/uscxml/debug/Breakpoint.h +++ /dev/null @@ -1,95 +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 - */ - -#ifndef BREAKPOINT_H_VR7K7T1X -#define BREAKPOINT_H_VR7K7T1X - -#include <string> // for string -#include "uscxml/Common.h" // for USCXML_API -#include "uscxml/Interpreter.h" -#include "DOM/Element.hpp" // for Element -#include "uscxml/messages/Data.h" // for Data - -namespace uscxml { - -class USCXML_API Breakpoint { -public: - - enum When { - UNDEF_WHEN, AFTER, BEFORE, ON - }; - - enum Subject { - UNDEF_SUBJECT, STATE, TRANSITION, STABLE, MICROSTEP, EVENT, INVOKER, EXECUTABLE - }; - - enum Action { - UNDEF_ACTION, ENTER, EXIT, INVOKE, UNINVOKE - }; - - Breakpoint() { - subject = UNDEF_SUBJECT; - when = UNDEF_WHEN; - action = UNDEF_ACTION; - } - Breakpoint(const Data& data); - - // would we match the given breakpoint as well? - bool matches(Interpreter interpreter, const Breakpoint& other) const; - - Data toData() const; - - bool operator<(const Breakpoint& other) const { - return (toData() < other.toData()); - } - - operator bool() { - return (subject != UNDEF_SUBJECT || - when != UNDEF_WHEN || - action != UNDEF_ACTION); - } - - mutable bool enabled; - - When when; - Subject subject; - Action action; - - Arabica::DOM::Element<std::string> element; - - std::string invokeId; - std::string invokeType; - - std::string eventName; - - std::string executableName; - std::string executableXPath; - - std::string stateId; - std::string transSourceId; - std::string transTargetId; - - std::string condition; -}; - -} - - - -#endif /* end of include guard: BREAKPOINT_H_VR7K7T1X */ diff --git a/src/uscxml/debug/Complexity.cpp b/src/uscxml/debug/Complexity.cpp deleted file mode 100644 index 232260c..0000000 --- a/src/uscxml/debug/Complexity.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/** -* @file -* @author 2012-2015 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 "Complexity.h" -#include "uscxml/dom/DOMUtils.h" - -#include <boost/algorithm/string.hpp> - -namespace uscxml { - -using namespace Arabica::DOM; -using namespace Arabica::XPath; - -std::list<std::set<Element<std::string> > > Complexity::getAllConfigurations(const Arabica::DOM::Element<std::string>& root) { - - std::list<std::set<Element<std::string> > > allConfigurations; - std::string nsPrefix = (root.getPrefix().size() > 0 ? root.getPrefix() + ":" : ""); - std::string localName = root.getLocalName(); - bool isAtomic = true; - - NodeList<std::string> children = root.getChildNodes(); - for (size_t i = 0; i < children.getLength(); i++) { - if (children.item(i).getNodeType() != Node_base::ELEMENT_NODE) - continue; - Element<std::string> childElem(children.item(i)); - if (childElem.getTagName() == nsPrefix + "state" || - childElem.getTagName() == nsPrefix + "parallel" || - childElem.getTagName() == nsPrefix + "final") { - // nested child state - std::list<std::set<Element<std::string> > > nestedConfigurations = getAllConfigurations(childElem); - isAtomic = false; - if (localName == "parallel" && allConfigurations.size() > 0) { - // for every nested configuration, every new nested is valid - std::list<std::set<Element<std::string> > > combinedConfigurations; - for (std::list<std::set<Element<std::string> > >::iterator existIter = allConfigurations.begin(); existIter != allConfigurations.end(); existIter++) { - std::set<Element<std::string> > existingConfig = *existIter; - - for (std::list<std::set<Element<std::string> > >::iterator newIter = nestedConfigurations.begin(); newIter != nestedConfigurations.end(); newIter++) { - - std::set<Element<std::string> > newConfig = *newIter; - std::set<Element<std::string> > combinedSet; - combinedSet.insert(existingConfig.begin(), existingConfig.end()); - combinedSet.insert(newConfig.begin(), newConfig.end()); - - combinedConfigurations.push_back(combinedSet); - } - } - allConfigurations = combinedConfigurations; - } else { - // just add nested configurations and this - for (std::list<std::set<Element<std::string> > >::iterator newIter = nestedConfigurations.begin(); newIter != nestedConfigurations.end(); newIter++) { - std::set<Element<std::string> > newConfig = *newIter; - if (localName != "scxml") - newConfig.insert(root); - allConfigurations.push_back(newConfig); - } - } - } - } - - if (isAtomic) { - std::set<Element<std::string> > soleConfig; - soleConfig.insert(root); - allConfigurations.push_back(soleConfig); - } - return allConfigurations; -} - -std::map<size_t, size_t> Complexity::getTransitionHistogramm(const Arabica::DOM::Element<std::string>& root) { - std::map<size_t, size_t> histogram; - std::string nameSpace; - - std::list<std::set<Element<std::string> > > allConfig = Complexity::getAllConfigurations(root); - - // for every legal configuration, count the transitions - for (std::list<std::set<Element<std::string> > >::iterator confIter = allConfig.begin(); confIter != allConfig.end(); confIter++) { - NodeSet<std::string> configNodeSet; - std::set<Element<std::string> > config = *confIter; - for (std::set<Element<std::string> >::iterator elemIter = config.begin(); elemIter != config.end(); elemIter++) { - configNodeSet.push_back(*elemIter); - if (nameSpace.size() == 0 && elemIter->getPrefix().size() > 0) - nameSpace = elemIter->getPrefix() + ":"; - } - NodeSet<std::string> transitions = DOMUtils::filterChildElements(nameSpace + "transition", configNodeSet); - histogram[transitions.size()]++; - } - - return histogram; -} - - -uint64_t Complexity::stateMachineComplexity(InterpreterImpl* interpreter, Variant variant) { - Arabica::DOM::Element<std::string> root = interpreter->getDocument().getDocumentElement(); - - Arabica::XPath::NodeSet<std::string> reachable; - - if (variant & IGNORE_UNREACHABLE) { - reachable = interpreter->getReachableStates(); - } - - Complexity complexity = calculateStateMachineComplexity(root, reachable); - uint64_t value = complexity.value; - - if (!(variant & IGNORE_HISTORY)) { - for (std::list<uint64_t>::const_iterator histIter = complexity.history.begin(); histIter != complexity.history.end(); histIter++) { - value *= *histIter; - } - } - - if (!(variant & IGNORE_NESTED_DATA)) { - bool ignoreNestedData = false; - if (root.getLocalName() == "scxml" && (!HAS_ATTR_CAST(root, "binding") || boost::to_lower_copy(ATTR_CAST(root, "binding")) == "early")) { - ignoreNestedData = true; - } - - if (!ignoreNestedData) { - uint64_t power = complexity.nestedData; - while(power--) { - value *= 2; - } - } - } - - return value; -} - -Complexity Complexity::calculateStateMachineComplexity(const Arabica::DOM::Element<std::string>& root, const Arabica::XPath::NodeSet<std::string>& reachable) { - Complexity complexity; - - bool hasFlatHistory = false; - bool hasDeepHistory = false; - bool hasNestedData = false; - - Arabica::DOM::NodeList<std::string> childElems = root.getChildNodes(); - for (size_t i = 0; i < childElems.getLength(); i++) { - if (childElems.item(i).getNodeType() != Node_base::ELEMENT_NODE) - continue; - Element<std::string> childElem = Element<std::string>(childElems.item(i)); - if (InterpreterImpl::isHistory(childElem)) { - if (HAS_ATTR(childElem, "type") && ATTR(childElem, "type") == "deep") { - hasDeepHistory = true; - } else { - hasFlatHistory = true; - } - } - if (!hasNestedData && childElem.getLocalName() == "datamodel") { - Arabica::DOM::NodeList<std::string> dataElemChilds = childElem.getChildNodes(); - for (size_t j = 0; j < dataElemChilds.getLength(); j++) { - if (dataElemChilds.item(j).getLocalName() == "data") - hasNestedData = true; - } - } - } - - if (hasNestedData) - complexity.nestedData++; - - if (reachable.size() > 0 && !InterpreterImpl::isMember(root, reachable)) { - return 0; - } else if (InterpreterImpl::isCompound(root) || TAGNAME(root) == "scxml") { - // compounds can be in any of the child state -> add - NodeSet<std::string> childs = InterpreterImpl::getChildStates(root); - for (size_t i = 0; i < childs.size(); i++) { - complexity += calculateStateMachineComplexity(Element<std::string>(childs[i]), reachable); - } - if (hasFlatHistory) { - complexity.history.push_back(childs.size()); - } - if (hasDeepHistory) { - complexity.history.push_back(complexity.value); - } - } else if (InterpreterImpl::isParallel(root)) { - // parallels are in all states -> multiply - NodeSet<std::string> childs = InterpreterImpl::getChildStates(root); - complexity.value = 1; - for (size_t i = 0; i < childs.size(); i++) { - complexity *= calculateStateMachineComplexity(Element<std::string>(childs[i]), reachable); - } - if (hasDeepHistory) { - complexity.history.push_back(complexity.value); - } - - } else if (InterpreterImpl::isAtomic(root)) { - return 1; - } - - return complexity; -} - -}
\ No newline at end of file diff --git a/src/uscxml/debug/Complexity.h b/src/uscxml/debug/Complexity.h deleted file mode 100644 index de05692..0000000 --- a/src/uscxml/debug/Complexity.h +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @file - * @author 2012-2015 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) - * @copyright Simplified BSD - * - * @cond - * This program is free software: you can redistribute it and/or modify - * it under the terms of the FreeBSD license as published by the FreeBSD - * project. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the FreeBSD license along with this - * program. If not, see <http://www.opensource.org/licenses/bsd-license>. - * @endcond - */ - -#ifndef COMPLEXITY_H_F972C065 -#define COMPLEXITY_H_F972C065 - -#include "uscxml/Common.h" // for USCXML_API -#include "uscxml/Interpreter.h" - -namespace uscxml { - -class USCXML_API Complexity { -public: - - enum Variant { - IGNORE_NOTHING = 0x0000, - IGNORE_HISTORY = 0x0001, - IGNORE_NESTED_DATA = 0x0002, - IGNORE_UNREACHABLE = 0x0004, - }; - - Complexity() : value(0), nestedData(0) {} - Complexity(uint64_t value) : value(value), nestedData(0) {} - - Complexity& operator+=(const Complexity& rhs) { - value += rhs.value; - nestedData += rhs.nestedData; - history.insert(history.end(), rhs.history.begin(), rhs.history.end()); - return *this; - } - - Complexity& operator*=(const Complexity& rhs) { - value *= rhs.value; - nestedData += rhs.nestedData; - history.insert(history.end(), rhs.history.begin(), rhs.history.end()); - return *this; - } - - static uint64_t stateMachineComplexity(const Interpreter& interpreter, Complexity::Variant variant = IGNORE_NOTHING) { - return stateMachineComplexity(interpreter.getImpl().get()); - } - static uint64_t stateMachineComplexity(InterpreterImpl* interpreter, Complexity::Variant variant = IGNORE_NOTHING); - - static std::list<std::set<Arabica::DOM::Element<std::string> > > getAllConfigurations(const Arabica::DOM::Element<std::string>& root); - static std::map<size_t, size_t> getTransitionHistogramm(const Arabica::DOM::Element<std::string>& root); - -protected: - static Complexity calculateStateMachineComplexity(const Arabica::DOM::Element<std::string>& root, const Arabica::XPath::NodeSet<std::string>& reachable); - - uint64_t value; - uint64_t nestedData; - std::list<uint64_t> history; -}; - -inline Complexity::Variant operator | ( Complexity::Variant lhs, Complexity::Variant rhs ) { - // Cast to int first otherwise we'll just end up recursing - return static_cast< Complexity::Variant >( static_cast< int >( lhs ) | static_cast< int >( rhs ) ); -} - -} - -#endif /* end of include guard: COMPLEXITY_H_F972C065 */ diff --git a/src/uscxml/debug/DebugSession.cpp b/src/uscxml/debug/DebugSession.cpp deleted file mode 100644 index 6e81563..0000000 --- a/src/uscxml/debug/DebugSession.cpp +++ /dev/null @@ -1,378 +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 "uscxml/debug/DebugSession.h" -#include "uscxml/debug/Debugger.h" - -namespace uscxml { - -void DebugSession::checkBreakpoints(const std::list<Breakpoint> qualifiedBreakpoints) { - std::list<Breakpoint>::const_iterator qualifiedBreakpointIter = qualifiedBreakpoints.begin(); - - if (!_breakpointsEnabled) - return; - - while(qualifiedBreakpointIter != qualifiedBreakpoints.end()) { - const Breakpoint& qualifiedBreakpoint = *qualifiedBreakpointIter++; - - // check if one of the user-supplied breakpoints match - bool userBreakpointMatched = false; - Data replyData; - - if (_skipTo) { - if (_skipTo.matches(_interpreter, qualifiedBreakpoint)) { - replyData.compound["breakpoint"] = _skipTo.toData(); - replyData.compound["qualified"] = qualifiedBreakpoint.toData(); - breakExecution(replyData); - _skipTo = Breakpoint(); - } - continue; - } - - std::set<Breakpoint>::const_iterator breakpointIter = _breakPoints.begin(); - while(breakpointIter != _breakPoints.end()) { - const Breakpoint& breakpoint = *breakpointIter++; - if (!breakpoint.enabled) - continue; - if (breakpoint.matches(_interpreter, qualifiedBreakpoint)) { - // do we have a condition? - - replyData.compound["breakpoint"] = breakpoint.toData(); - replyData.compound["qualified"] = qualifiedBreakpoint.toData(); - - userBreakpointMatched = true; - breakExecution(replyData); - } - } - if (_isStepping && !userBreakpointMatched) { - replyData.compound["qualified"] = qualifiedBreakpoint.toData(); - breakExecution(replyData); - - } - } -} - -void DebugSession::breakExecution(Data replyData) { - tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - - Arabica::XPath::NodeSet<std::string> basicConf = _interpreter.getBasicConfiguration(); - for (size_t i = 0; i < basicConf.size(); i++) { - Arabica::DOM::Element<std::string> element = Arabica::DOM::Element<std::string>(basicConf[i]); - if (element.hasAttribute("id")) { - replyData.compound["basicStates"].array.push_back(Data(element.getAttribute("id"), Data::VERBATIM)); - } - } - - Arabica::XPath::NodeSet<std::string> activeConf = _interpreter.getConfiguration(); - for (size_t i = 0; i < activeConf.size(); i++) { - Arabica::DOM::Element<std::string> element = Arabica::DOM::Element<std::string>(activeConf[i]); - if (element.hasAttribute("id")) { - replyData.compound["activeStates"].array.push_back(Data(element.getAttribute("id"), Data::VERBATIM)); - } - } - - replyData.compound["replyType"] = Data("breakpoint", Data::VERBATIM); - _debugger->pushData(shared_from_this(), replyData); - _resumeCond.wait(_mutex); -} - -Data DebugSession::debugPrepare(const Data& data) { - Data replyData; - - if (!data.hasKey("xml") && !data.hasKey("url")) { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - replyData.compound["reason"] = Data("No XML or URL given", Data::VERBATIM); - return replyData; - } - - debugStop(data); - - _isAttached = false; - - if (data.hasKey("xml")) { - _interpreter = Interpreter::fromXML(data.at("xml").atom, ""); - } else if (data.hasKey("url")) { - _interpreter = Interpreter::fromURL(data.at("url").atom); - } else { - _interpreter = Interpreter(); - } - - if (_interpreter) { - // register ourself as a monitor - _interpreter.addMonitor(_debugger); - _debugger->attachSession(_interpreter, shared_from_this()); - if (data.hasKey("url")) { - // this allows to resolve relative external reources - _interpreter.setSourceURL(data.at("url").atom); - } - replyData.compound["status"] = Data("success", Data::VERBATIM); - } else { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - } - - return replyData; -} - -Data DebugSession::debugAttach(const Data& data) { - Data replyData; - _isAttached = true; - - if (!data.hasKey("attach")) { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - replyData.compound["reason"] = Data("No id to attach to given", Data::VERBATIM); - return replyData; - } - - std::string interpreterId = data.at("attach").atom; - bool interpreterFound = false; - - // find interpreter for sessionid - std::map<std::string, boost::weak_ptr<InterpreterImpl> > instances = Interpreter::getInstances(); - for (std::map<std::string, boost::weak_ptr<InterpreterImpl> >::iterator instIter = instances.begin(); - instIter != instances.end(); - instIter++) { - - boost::shared_ptr<InterpreterImpl> instance = instIter->second.lock(); - if (instance && instance->getSessionId() == interpreterId) { - _interpreter = instance; - _debugger->attachSession(_interpreter, shared_from_this()); - interpreterFound = true; - break; - } - } - - if (!interpreterFound) { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - replyData.compound["reason"] = Data("No interpreter with given id found", Data::VERBATIM); - } else { - replyData.compound["xml"].node = _interpreter.getDocument(); - replyData.compound["status"] = Data("success", Data::VERBATIM); - } - - return replyData; -} - -Data DebugSession::debugDetach(const Data& data) { - Data replyData; - _isAttached = false; - - _debugger->detachSession(_interpreter); - replyData.compound["status"] = Data("success", Data::VERBATIM); - return replyData; -} - -Data DebugSession::debugStart(const Data& data) { - Data replyData; - - if (_isAttached) { - replyData.compound["reason"] = Data("Already started when attached", Data::VERBATIM); - replyData.compound["status"] = Data("failure", Data::VERBATIM); - } else if (!_interpreter) { - replyData.compound["reason"] = Data("No interpreter attached or loaded", Data::VERBATIM); - replyData.compound["status"] = Data("failure", Data::VERBATIM); - } else { - _interpreter.start(); - replyData.compound["status"] = Data("success", Data::VERBATIM); - } - - return replyData; -} - -Data DebugSession::debugStop(const Data& data) { - Data replyData; - - if (_interpreter) { - // detach from old intepreter - _debugger->detachSession(_interpreter); - } - - if (_interpreter && !_isAttached) - _interpreter.stop(); - // unblock - _resumeCond.notify_all(); - - _skipTo = Breakpoint(); - replyData.compound["status"] = Data("success", Data::VERBATIM); - - // calls destructor - _interpreter = Interpreter(); - - return replyData; -} - -Data DebugSession::debugStep(const Data& data) { - tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - - stepping(true); - _resumeCond.notify_one(); - - Data replyData; - if (_interpreter) { - // register ourself as a monitor - if (!_interpreter.isRunning()) - _interpreter.start(); - replyData.compound["status"] = Data("success", Data::VERBATIM); - } else { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - } - return replyData; -} - -Data DebugSession::debugResume(const Data& data) { - tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - - stepping(false); - - Data replyData; - replyData.compound["status"] = Data("success", Data::VERBATIM); - - _resumeCond.notify_one(); - return replyData; -} - - -Data DebugSession::debugPause(const Data& data) { - tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - - _skipTo = Breakpoint(); - stepping(true); - - Data replyData; - replyData.compound["status"] = Data("success", Data::VERBATIM); - - return replyData; -} - -Data DebugSession::skipToBreakPoint(const Data& data) { - tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - - _skipTo = Breakpoint(data); - - Data replyData; - replyData.compound["status"] = Data("success", Data::VERBATIM); - - _resumeCond.notify_one(); - return replyData; -} - -Data DebugSession::addBreakPoint(const Data& data) { - Breakpoint breakpoint(data); - - Data replyData; - if (_breakPoints.find(breakpoint) == _breakPoints.end()) { - _breakPoints.insert(breakpoint); - replyData.compound["status"] = Data("success", Data::VERBATIM); - - } else { - replyData.compound["reason"] = Data("Breakpoint already exists", Data::VERBATIM); - replyData.compound["status"] = Data("failure", Data::VERBATIM); - } - return replyData; -} - -Data DebugSession::removeBreakPoint(const Data& data) { - Breakpoint breakpoint(data); - - Data replyData; - if (_breakPoints.find(breakpoint) != _breakPoints.end()) { - _breakPoints.erase(breakpoint); - replyData.compound["status"] = Data("success", Data::VERBATIM); - } else { - replyData.compound["reason"] = Data("No such breakpoint", Data::VERBATIM); - replyData.compound["status"] = Data("failure", Data::VERBATIM); - } - return replyData; -} - -Data DebugSession::enableBreakPoint(const Data& data) { - Breakpoint breakpoint(data); - - Data replyData; - if (_breakPoints.find(breakpoint) != _breakPoints.end()) { - _breakPoints.find(breakpoint)->enabled = true; - replyData.compound["status"] = Data("success", Data::VERBATIM); - } else { - replyData.compound["reason"] = Data("No such breakpoint", Data::VERBATIM); - replyData.compound["status"] = Data("failure", Data::VERBATIM); - } - - return replyData; -} -Data DebugSession::disableBreakPoint(const Data& data) { - Breakpoint breakpoint(data); - - Data replyData; - if (_breakPoints.find(breakpoint) != _breakPoints.end()) { - _breakPoints.find(breakpoint)->enabled = false; - replyData.compound["status"] = Data("success", Data::VERBATIM); - } else { - replyData.compound["reason"] = Data("No such breakpoint", Data::VERBATIM); - replyData.compound["status"] = Data("failure", Data::VERBATIM); - } - - return replyData; -} -Data DebugSession::enableAllBreakPoints() { - Data replyData; - - _breakpointsEnabled = true; - replyData.compound["status"] = Data("success", Data::VERBATIM); - - return replyData; -} -Data DebugSession::disableAllBreakPoints() { - Data replyData; - - _breakpointsEnabled = false; - replyData.compound["status"] = Data("success", Data::VERBATIM); - - return replyData; -} - -Data DebugSession::debugEval(const Data& data) { - Data replyData; - - if (!data.hasKey("expression")) { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - replyData.compound["reason"] = Data("No expression given", Data::VERBATIM); - return replyData; - } - - std::string expr = data.at("expression").atom; - - if (!_interpreter) { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - replyData.compound["reason"] = Data("No interpreter running", Data::VERBATIM); - } else if (!_interpreter.getDataModel()) { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - replyData.compound["reason"] = Data("No datamodel available", Data::VERBATIM); - } else { - try { - replyData.compound["eval"] = _interpreter.getDataModel().getStringAsData(expr); - } catch (Event e) { - replyData.compound["eval"] = e.data; - replyData.compound["eval"].compound["error"] = Data(e.name, Data::VERBATIM); - } - replyData.compound["status"] = Data("success", Data::VERBATIM); - } - return replyData; -} - - -}
\ No newline at end of file diff --git a/src/uscxml/debug/DebugSession.h b/src/uscxml/debug/DebugSession.h deleted file mode 100644 index 1fb4f4d..0000000 --- a/src/uscxml/debug/DebugSession.h +++ /dev/null @@ -1,99 +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 - */ - -#ifndef DEBUGSESSION_H_M8YHEGV6 -#define DEBUGSESSION_H_M8YHEGV6 - -#include "uscxml/debug/Breakpoint.h" -#include "uscxml/Interpreter.h" -#include <time.h> - -namespace uscxml { - -class Debugger; - -class USCXML_API DebugSession : public boost::enable_shared_from_this<DebugSession> { -public: - DebugSession() { - _isStepping = false; - _isAttached = false; - _breakpointsEnabled = true; - _markedForDeletion = false; - _debugger = NULL; - } - - void stepping(bool enable) { - _isStepping = enable; - } - - void checkBreakpoints(const std::list<Breakpoint> qualifiedBreakpoints); - - Data debugPrepare(const Data& data); - Data debugAttach(const Data& data); - Data debugDetach(const Data& data); - Data debugStart(const Data& data); - Data debugStop(const Data& data); - Data debugStep(const Data& data); - Data debugResume(const Data& data); - Data debugPause(const Data& data); - Data skipToBreakPoint(const Data& data); - Data addBreakPoint(const Data& data); - Data removeBreakPoint(const Data& data); - Data enableBreakPoint(const Data& data); - Data disableBreakPoint(const Data& data); - Data enableAllBreakPoints(); - Data disableAllBreakPoints(); - Data debugEval(const Data& data); - - void setDebugger(Debugger* debugger) { - _debugger = debugger; - } - - Interpreter getInterpreter() { - return _interpreter; - } - - void markForDeletion(bool mark) { - _markedForDeletion = mark; - } - -protected: - void breakExecution(Data replyData); - - bool _isStepping; - bool _isAttached; - bool _breakpointsEnabled; - - tthread::condition_variable _resumeCond; - tthread::recursive_mutex _runMutex; - tthread::recursive_mutex _mutex; - - bool _markedForDeletion; - Debugger* _debugger; - Interpreter _interpreter; - std::set<Breakpoint> _breakPoints; - Breakpoint _skipTo; - -}; - - -} - - -#endif /* end of include guard: DEBUGSESSION_H_M8YHEGV6 */ diff --git a/src/uscxml/debug/Debugger.cpp b/src/uscxml/debug/Debugger.cpp deleted file mode 100644 index f8b13fe..0000000 --- a/src/uscxml/debug/Debugger.cpp +++ /dev/null @@ -1,256 +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 "uscxml/debug/Debugger.h" -#include "uscxml/dom/DOMUtils.h" -#include "uscxml/debug/DebugSession.h" - -namespace uscxml { - -void Debugger::afterCompletion(Interpreter interpreter) { - boost::shared_ptr<DebugSession> session = getSession(interpreter); - if (!session) - return; - - Data msg; - msg.compound["replyType"] = Data("finished", Data::VERBATIM); - pushData(session, msg); -} - -std::list<Breakpoint> getQualifiedStateBreakpoints(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, Breakpoint breakpointTemplate) { - std::list<Breakpoint> breakpoints; - - Breakpoint bp = breakpointTemplate; // copy base as template - bp.stateId = ATTR(state, "id"); - bp.element = state; - bp.subject = Breakpoint::STATE; - breakpoints.push_back(bp); - - return breakpoints; -} - -std::list<Breakpoint> getQualifiedInvokeBreakpoints(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string invokeId, Breakpoint breakpointTemplate) { - std::list<Breakpoint> breakpoints; - - Breakpoint bp = breakpointTemplate; // copy base as template - bp.subject = Breakpoint::INVOKER; - bp.element = invokeElem; - bp.invokeId = invokeId; - - if (HAS_ATTR(invokeElem, "type")) { - bp.invokeType = ATTR(invokeElem, "type"); - } else if (HAS_ATTR(invokeElem, "typeexpr")) { - bp.invokeType = interpreter.getDataModel().evalAsString(ATTR(invokeElem, "typeexpr")); - } - - breakpoints.push_back(bp); - - return breakpoints; -} - -std::list<Breakpoint> getQualifiedTransBreakpoints(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, Breakpoint breakpointTemplate) { - std::list<Breakpoint> breakpoints; - - Arabica::DOM::Element<std::string> source(interpreter.getImpl()->getSourceState(transition)); - Arabica::XPath::NodeSet<std::string> targets = interpreter.getImpl()->getTargetStates(transition); - - for (size_t j = 0; j < targets.size(); j++) { - Arabica::DOM::Element<std::string> target(targets[j]); - - Breakpoint bp = breakpointTemplate; // copy base as template - bp.element = transition; - bp.transSourceId = ATTR(source, "id"); - bp.transTargetId = ATTR(target, "id"); - bp.subject = Breakpoint::TRANSITION; - - breakpoints.push_back(bp); - } - - return breakpoints; -} - -void Debugger::beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, bool moreComing) { - handleTransition(interpreter, transition, Breakpoint::BEFORE); -} -void Debugger::afterTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, bool moreComing) { - handleTransition(interpreter, transition, Breakpoint::AFTER); -} -void Debugger::beforeExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& content) { - handleExecutable(interpreter, Arabica::DOM::Element<std::string>(content), Breakpoint::BEFORE); -} -void Debugger::afterExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& content) { - handleExecutable(interpreter, Arabica::DOM::Element<std::string>(content), Breakpoint::AFTER); -} -void Debugger::beforeExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing) { - handleState(interpreter, state, Breakpoint::BEFORE, Breakpoint::EXIT); -} -void Debugger::afterExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing) { - handleState(interpreter, state, Breakpoint::AFTER, Breakpoint::EXIT); -} -void Debugger::beforeEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing) { - handleState(interpreter, state, Breakpoint::BEFORE, Breakpoint::ENTER); -} -void Debugger::afterEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing) { - handleState(interpreter, state, Breakpoint::AFTER, Breakpoint::ENTER); -} -void Debugger::beforeUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) { - handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::BEFORE, Breakpoint::UNINVOKE); -} -void Debugger::afterUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) { - handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::AFTER, Breakpoint::UNINVOKE); -} -void Debugger::beforeInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) { - handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::BEFORE, Breakpoint::INVOKE); -} -void Debugger::afterInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) { - handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::AFTER, Breakpoint::INVOKE); -} -void Debugger::onStableConfiguration(Interpreter interpreter) { - handleStable(interpreter, Breakpoint::ON); -} -void Debugger::beforeMicroStep(Interpreter interpreter) { - handleMicrostep(interpreter, Breakpoint::BEFORE); -} -void Debugger::afterMicroStep(Interpreter interpreter) { - handleMicrostep(interpreter, Breakpoint::AFTER); -} -void Debugger::beforeProcessingEvent(Interpreter interpreter, const Event& event) { - handleEvent(interpreter, event, Breakpoint::BEFORE); -} - -void Debugger::handleExecutable(Interpreter interpreter, - const Arabica::DOM::Element<std::string>& execContentElem, - Breakpoint::When when) { - if (!interpreter.isRunning()) - return; - boost::shared_ptr<DebugSession> session = getSession(interpreter); - if (!session) - return; - - std::list<Breakpoint> breakpoints; - - Breakpoint breakpoint; - breakpoint.when = when; - breakpoint.element = execContentElem; - breakpoint.executableName = execContentElem.getLocalName(); - breakpoint.subject = Breakpoint::EXECUTABLE; - breakpoints.push_back(breakpoint); - - session->checkBreakpoints(breakpoints); - -} - -void Debugger::handleEvent(Interpreter interpreter, const Event& event, Breakpoint::When when) { - if (!interpreter.isRunning()) - return; - boost::shared_ptr<DebugSession> session = getSession(interpreter); - if (!session) - return; - - std::list<Breakpoint> breakpoints; - - Breakpoint breakpoint; - breakpoint.when = when; - breakpoint.eventName = event.name; - breakpoint.subject = Breakpoint::EVENT; - breakpoints.push_back(breakpoint); - - session->checkBreakpoints(breakpoints); - -} - -void Debugger::handleStable(Interpreter interpreter, Breakpoint::When when) { - if (!interpreter.isRunning()) - return; - boost::shared_ptr<DebugSession> session = getSession(interpreter); - if (!session) - return; - - std::list<Breakpoint> breakpoints; - - Breakpoint breakpoint; - breakpoint.when = when; - breakpoint.subject = Breakpoint::STABLE; - breakpoints.push_back(breakpoint); - - session->checkBreakpoints(breakpoints); -} - -void Debugger::handleMicrostep(Interpreter interpreter, Breakpoint::When when) { - if (!interpreter.isRunning()) - return; - boost::shared_ptr<DebugSession> session = getSession(interpreter); - if (!session) - return; - - std::list<Breakpoint> breakpoints; - - Breakpoint breakpoint; - breakpoint.when = when; - breakpoint.subject = Breakpoint::MICROSTEP; - breakpoints.push_back(breakpoint); - - session->checkBreakpoints(breakpoints); -} - -void Debugger::handleTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, Breakpoint::When when) { - if (!interpreter.isRunning()) - return; - boost::shared_ptr<DebugSession> session = getSession(interpreter); - if (!session) - return; - - Breakpoint breakpointTemplate; - breakpointTemplate.when = when; - std::list<Breakpoint> qualifiedBreakpoints = getQualifiedTransBreakpoints(interpreter, transition, breakpointTemplate); - session->checkBreakpoints(qualifiedBreakpoints); -} - -void Debugger::handleState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, Breakpoint::When when, Breakpoint::Action action) { - if (!interpreter.isRunning()) - return; - boost::shared_ptr<DebugSession> session = getSession(interpreter); - if (!session) - return; - - Breakpoint breakpointTemplate; - breakpointTemplate.when = when; - breakpointTemplate.action = action; - std::list<Breakpoint> qualifiedBreakpoints = getQualifiedStateBreakpoints(interpreter, state, breakpointTemplate); - session->checkBreakpoints(qualifiedBreakpoints); - -} - -void Debugger::handleInvoke(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeId, Breakpoint::When when, Breakpoint::Action action) { - if (!interpreter.isRunning()) - return; - boost::shared_ptr<DebugSession> session = getSession(interpreter); - if (!session) - return; - - Breakpoint breakpointTemplate; - breakpointTemplate.when = when; - breakpointTemplate.action = action; - std::list<Breakpoint> qualifiedBreakpoints = getQualifiedInvokeBreakpoints(interpreter, invokeElem, invokeId, breakpointTemplate); - session->checkBreakpoints(qualifiedBreakpoints); - -} - - -}
\ No newline at end of file diff --git a/src/uscxml/debug/Debugger.h b/src/uscxml/debug/Debugger.h deleted file mode 100644 index 03846e5..0000000 --- a/src/uscxml/debug/Debugger.h +++ /dev/null @@ -1,105 +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 - */ - -#ifndef DEBUGGERMONITOR_H_Z050WPFH -#define DEBUGGERMONITOR_H_Z050WPFH - -#include "uscxml/messages/Data.h" // for Data -#include "uscxml/messages/Event.h" // for Event -#include "uscxml/Interpreter.h" -#include "uscxml/debug/Breakpoint.h" - -namespace uscxml { - -class DebugSession; - -class USCXML_API Debugger : public InterpreterMonitor { -public: - Debugger() { - } - virtual ~Debugger() {} - - virtual void attachSession(Interpreter interpreter, boost::shared_ptr<DebugSession> session) { - tthread::lock_guard<tthread::recursive_mutex> lock(_sessionMutex); - _sessionForInterpreter[interpreter] = session; - } - - virtual void detachSession(Interpreter interpreter) { - tthread::lock_guard<tthread::recursive_mutex> lock(_sessionMutex); - _sessionForInterpreter.erase(interpreter); - } - - virtual boost::shared_ptr<DebugSession> getSession(Interpreter interpreter) { - tthread::lock_guard<tthread::recursive_mutex> lock(_sessionMutex); - if (_sessionForInterpreter.find(interpreter) != _sessionForInterpreter.end()) - return _sessionForInterpreter[interpreter]; - return boost::shared_ptr<DebugSession>(); - } - - virtual void pushData(boost::shared_ptr<DebugSession> session, Data pushData) = 0; - - // InterpreterMonitor - virtual void beforeProcessingEvent(Interpreter interpreter, const Event& event); - virtual void beforeMicroStep(Interpreter interpreter); - virtual void beforeExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing); - virtual void afterExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing); - virtual void beforeExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& element); - virtual void afterExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& element); - virtual void beforeUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); - virtual void afterUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); - virtual void beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, bool moreComing); - virtual void afterTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, bool moreComing); - virtual void beforeEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing); - virtual void afterEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, bool moreComing); - virtual void beforeInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); - virtual void afterInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); - virtual void afterMicroStep(Interpreter interpreter); - virtual void onStableConfiguration(Interpreter interpreter); - virtual void beforeCompletion(Interpreter interpreter) {} - virtual void afterCompletion(Interpreter interpreter); - -protected: - - void handleTransition(Interpreter interpreter, - const Arabica::DOM::Element<std::string>& transition, - Breakpoint::When when); - void handleState(Interpreter interpreter, - const Arabica::DOM::Element<std::string>& state, - Breakpoint::When when, - Breakpoint::Action action); - void handleInvoke(Interpreter interpreter, - const Arabica::DOM::Element<std::string>& invokeElem, - const std::string& invokeId, - Breakpoint::When when, - Breakpoint::Action action); - void handleExecutable(Interpreter interpreter, - const Arabica::DOM::Element<std::string>& execContentElem, - Breakpoint::When when); - void handleStable(Interpreter interpreter, Breakpoint::When when); - void handleMicrostep(Interpreter interpreter, Breakpoint::When when); - void handleEvent(Interpreter interpreter, const Event& event, Breakpoint::When when); - - tthread::recursive_mutex _sessionMutex; - std::map<Interpreter, boost::shared_ptr<DebugSession> > _sessionForInterpreter; -}; - -} - - -#endif /* end of include guard: DEBUGGERMONITOR_H_Z050WPFH */ diff --git a/src/uscxml/debug/DebuggerServlet.cpp b/src/uscxml/debug/DebuggerServlet.cpp deleted file mode 100644 index 64426ff..0000000 --- a/src/uscxml/debug/DebuggerServlet.cpp +++ /dev/null @@ -1,262 +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 "uscxml/debug/DebuggerServlet.h" -#include "uscxml/debug/DebugSession.h" -#include "uscxml/UUID.h" -#include <boost/algorithm/string.hpp> - -namespace uscxml { - -void DebuggerServlet::pushData(boost::shared_ptr<DebugSession> session, Data pushData) { - std::cout << "trying to push " << pushData.at("replyType").atom << std::endl; - - if (!session) { - if (_sendQueues.size() > 0) // logging is not aware of its interpreter - _sendQueues.begin()->second.push(pushData); - } else { - _sendQueues[session].push(pushData); - } - - serverPushData(session); -} - -void DebuggerServlet::serverPushData(boost::shared_ptr<DebugSession> session) { - if (_sendQueues[session].isEmpty()) - return; - - if (!_clientConns[session]) - return; - - Data reply = _sendQueues[session].pop(); - std::cout << "pushing " << reply.at("replyType").atom << std::endl; - returnData(_clientConns[session], reply); - _clientConns[session] = HTTPServer::Request(); -} - -void DebuggerServlet::returnData(const HTTPServer::Request& request, Data replyData) { - HTTPServer::Reply reply(request); - - if (!replyData.hasKey("status")) { - replyData.compound["status"] = Data("success", Data::VERBATIM); - } - - std::cout << "<- " << replyData << std::endl; - - reply.content = Data::toJSON(replyData); - reply.headers["Access-Control-Allow-Origin"] = "*"; - reply.headers["Content-Type"] = "application/json"; - HTTPServer::reply(reply); -} - -bool DebuggerServlet::isCORS(const HTTPServer::Request& request) { - return (request.data.at("type").atom == "options" && - request.data.at("header").hasKey("Origin") && - request.data.at("header").hasKey("Access-Control-Request-Method")); -} - -void DebuggerServlet::handleCORS(const HTTPServer::Request& request) { - HTTPServer::Reply corsReply(request); - if (request.data.at("header").hasKey("Origin")) { - corsReply.headers["Access-Control-Allow-Origin"] = request.data.at("header").at("Origin").atom; - } else { - corsReply.headers["Access-Control-Allow-Origin"] = "*"; - } - if (request.data.at("header").hasKey("Access-Control-Request-Method")) - corsReply.headers["Access-Control-Allow-Methods"] = request.data.at("header").at("Access-Control-Request-Method").atom; - if (request.data.at("header").hasKey("Access-Control-Request-Headers")) - corsReply.headers["Access-Control-Allow-Headers"] = request.data.at("header").at("Access-Control-Request-Headers").atom; - - // std::cout << "CORS!" << std::endl << request << std::endl; - HTTPServer::reply(corsReply); -} - -bool DebuggerServlet::httpRecvRequest(const HTTPServer::Request& request) { - if (!request.data.hasKey("path")) - return false; // returnError(request); - - if (isCORS(request)) { - handleCORS(request); - return true; - } - - std::cout << request.data["path"] << ": " << request.data["content"] << std::endl; - - Data replyData; - // process request that don't need a session - if (false) { - } else if (boost::starts_with(request.data.at("path").atom, "/debug/connect")) { - processConnect(request); - return true; - } else if (boost::starts_with(request.data.at("path").atom, "/debug/sessions")) { - processListSessions(request); - return true; - } - - // get session or return error - if (false) { - } else if (!request.data.at("content").hasKey("session")) { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - replyData.compound["reason"] = Data("No session given", Data::VERBATIM); - } else if (_sessionForId.find(request.data.at("content").at("session").atom) == _sessionForId.end()) { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - replyData.compound["reason"] = Data("No such session", Data::VERBATIM); - } - if (!replyData.empty()) { - returnData(request, replyData); - return true; - } - - boost::shared_ptr<DebugSession> session = _sessionForId[request.data.at("content").at("session").atom]; - - if (false) { - } else if (boost::starts_with(request.data.at("path").atom, "/debug/poll")) { - // save long-standing client poll - _clientConns[session] = request; - serverPushData(session); - - } else if (boost::starts_with(request.data.at("path").atom, "/debug/disconnect")) { - processDisconnect(request); - - } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/enable/all")) { - replyData = session->enableAllBreakPoints(); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/disable/all")) { - replyData = session->disableAllBreakPoints(); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/skipto")) { - replyData = session->skipToBreakPoint(request.data["content"]); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/add")) { - replyData = session->addBreakPoint(request.data["content"]); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/remove")) { - replyData = session->removeBreakPoint(request.data["content"]); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/enable")) { - replyData = session->enableBreakPoint(request.data["content"]); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/disable")) { - replyData = session->disableBreakPoint(request.data["content"]); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/stop")) { - replyData = session->debugStop(request.data["content"]); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/prepare")) { - replyData = session->debugPrepare(request.data["content"]); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/attach")) { - replyData = session->debugAttach(request.data["content"]); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/start")) { - replyData = session->debugStart(request.data["content"]); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/step")) { - replyData = session->debugStep(request.data["content"]); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/pause")) { - replyData = session->debugPause(request.data["content"]); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/resume")) { - replyData = session->debugResume(request.data["content"]); - } else if (boost::starts_with(request.data.at("path").atom, "/debug/eval")) { - replyData = session->debugEval(request.data["content"]); - } - - if (!replyData.empty()) { - returnData(request, replyData); - return true; - } - - return true; -} - -// someone connected, create a new session -void DebuggerServlet::processConnect(const HTTPServer::Request& request) { - tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - std::string sessionId = UUID::getUUID(); - - _sessionForId[sessionId] = boost::shared_ptr<DebugSession>(new DebugSession()); - _sessionForId[sessionId]->setDebugger(this); - - Data replyData; - replyData.compound["session"] = Data(sessionId, Data::VERBATIM); - replyData.compound["status"] = Data("success", Data::VERBATIM); - returnData(request, replyData); -} - -void DebuggerServlet::processDisconnect(const HTTPServer::Request& request) { - tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - - Data replyData; - - if (!request.data.at("content").hasKey("session")) { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - replyData.compound["reason"] = Data("No session given", Data::VERBATIM); - returnData(request, replyData); - } - - std::string sessionId = request.data.at("content").at("session").atom; - - if (_sessionForId.find(sessionId) == _sessionForId.end()) { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - replyData.compound["reason"] = Data("No such session", Data::VERBATIM); - } else { - replyData.compound["status"] = Data("success", Data::VERBATIM); - detachSession(_sessionForId[sessionId]->getInterpreter()); - _sessionForId[sessionId]->debugStop(request.data["content"]); - _clientConns.erase(_sessionForId[sessionId]); - _sendQueues.erase(_sessionForId[sessionId]); - _sessionForId.erase(sessionId); - } - - returnData(request, replyData); -} - -void DebuggerServlet::processListSessions(const HTTPServer::Request& request) { - Data replyData; - - std::map<std::string, boost::weak_ptr<InterpreterImpl> > instances = Interpreter::getInstances(); - for (std::map<std::string, boost::weak_ptr<InterpreterImpl> >::iterator instIter = instances.begin(); - instIter != instances.end(); - instIter++) { - - boost::shared_ptr<InterpreterImpl> instance = instIter->second.lock(); - if (instance) { - Data sessionData; - sessionData.compound["name"] = Data(instance->getName(), Data::VERBATIM); - sessionData.compound["id"] = Data(instance->getSessionId(), Data::VERBATIM); - sessionData.compound["source"] = Data(instance->getSourceURL(), Data::VERBATIM); - sessionData.compound["xml"].node = instance->getDocument(); - - replyData.compound["sessions"].array.push_back(sessionData); - } - } - - replyData.compound["status"] = Data("success", Data::VERBATIM); - returnData(request, replyData); -} - -void DebuggerServlet::send(google::LogSeverity severity, const char* full_filename, - const char* base_filename, int line, - const struct ::tm* tm_time, - const char* message, size_t message_len) { - - // _sendQueue is thread-safe, not sure about ToString though - - LogMessage msg(severity, - full_filename, - base_filename, - line, - tm_time, - std::string(message, message_len), - ToString(severity, base_filename, line, tm_time, message, message_len)); - msg.compound["replyType"] = Data("log", Data::VERBATIM); - pushData(boost::shared_ptr<DebugSession>(), msg); -} - - -}
\ No newline at end of file diff --git a/src/uscxml/debug/DebuggerServlet.h b/src/uscxml/debug/DebuggerServlet.h deleted file mode 100644 index 8abe741..0000000 --- a/src/uscxml/debug/DebuggerServlet.h +++ /dev/null @@ -1,106 +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 - */ - -#ifndef DEBUGGERSERVLET_H_ATUMDA3G -#define DEBUGGERSERVLET_H_ATUMDA3G - -#include "uscxml/Common.h" -#include <glog/logging.h> - -#include "uscxml/server/HTTPServer.h" - -#include "uscxml/debug/Debugger.h" -#include "uscxml/concurrency/tinythread.h" - -namespace uscxml { - -class USCXML_API DebuggerServlet : public Debugger, public HTTPServlet, public google::LogSink { -public: - class LogMessage : public Data { - public: - LogMessage(google::LogSeverity severity, const char* full_filename, - const char* base_filename, int line, - const struct ::tm* tm_time, - std::string message, std::string formatted) { - - compound["severity"] = severity; - compound["fullFilename"] = Data(full_filename, Data::VERBATIM); - compound["baseFilename"] = Data(base_filename, Data::VERBATIM); - compound["line"] = line; - compound["message"] = Data(message, Data::VERBATIM); - compound["time"] = Data(mktime((struct ::tm*)tm_time), Data::INTERPRETED); - compound["formatted"] = Data(formatted, Data::VERBATIM); - } - }; - - virtual ~DebuggerServlet() {} - - // from Debugger - virtual void addBreakpoint(const Breakpoint& breakpoint) {}; - - bool isCORS(const HTTPServer::Request& request); - void handleCORS(const HTTPServer::Request& request); - - bool httpRecvRequest(const HTTPServer::Request& request); - void setURL(const std::string& url) { - _url = url; - } - - void pushData(boost::shared_ptr<DebugSession> session, Data pushData); - void returnData(const HTTPServer::Request& request, Data replyData); - - void processDisconnect(const HTTPServer::Request& request); - void processConnect(const HTTPServer::Request& request); - void processListSessions(const HTTPServer::Request& request); - -// void processDebugPrepare(const HTTPServer::Request& request); -// void processDebugAttach(const HTTPServer::Request& request); -// void processDebugStart(const HTTPServer::Request& request); -// void processDebugStop(const HTTPServer::Request& request); - -// void processDebugEval(const HTTPServer::Request& request); -// void processDebugStart(const HTTPServer::Request& request); -// void processDebugStop(const HTTPServer::Request& request); -// void processDebugStep(const HTTPServer::Request& request); -// void processDebugResume(const HTTPServer::Request& request); -// void processDebugPause(const HTTPServer::Request& request); -// void processAddBreakPoint(const HTTPServer::Request& request); -// void processRemoveBreakPoint(const HTTPServer::Request& request); -// void processPoll(const HTTPServer::Request& request); - - // Logsink - virtual void send(google::LogSeverity severity, const char* full_filename, - const char* base_filename, int line, - const struct ::tm* tm_time, - const char* message, size_t message_len); - -protected: - void serverPushData(boost::shared_ptr<DebugSession>); - - std::string _url; - std::map<boost::shared_ptr<DebugSession>, HTTPServer::Request> _clientConns; - std::map<boost::shared_ptr<DebugSession>, concurrency::BlockingQueue<Data> > _sendQueues; - std::map<std::string, boost::shared_ptr<DebugSession> > _sessionForId; - - tthread::recursive_mutex _mutex; -}; - -} - -#endif /* end of include guard: DEBUGGERSERVLET_H_ATUMDA3G */ diff --git a/src/uscxml/debug/InterpreterIssue.cpp b/src/uscxml/debug/InterpreterIssue.cpp index 1bb14f6..ce61af6 100644 --- a/src/uscxml/debug/InterpreterIssue.cpp +++ b/src/uscxml/debug/InterpreterIssue.cpp @@ -20,67 +20,128 @@ #include <string> #include "InterpreterIssue.h" -#include "uscxml/dom/DOMUtils.h" -#include "uscxml/debug/Complexity.h" +#include "uscxml/util/DOM.h" +#include "uscxml/util/String.h" +#include "uscxml/util/Predicates.h" +//#include "uscxml/debug/Complexity.h" #include "uscxml/Interpreter.h" -#include "uscxml/Factory.h" +#include "uscxml/plugins/Factory.h" -#include <XPath/XPath.hpp> -#include <DOM/Document.hpp> +#include <xercesc/dom/DOMDocument.hpp> namespace uscxml { -using namespace Arabica::XPath; -using namespace Arabica::DOM; +using namespace xercesc; -InterpreterIssue::InterpreterIssue(const std::string& msg, Arabica::DOM::Node<std::string> node, IssueSeverity severity, const std::string& specRef) : message(msg), node(node), severity(severity), specRef(specRef) { +InterpreterIssue::InterpreterIssue(const std::string& msg, DOMNode* node, IssueSeverity severity, const std::string& specRef) : message(msg), node(node), severity(severity), specRef(specRef) { if (node) xPath = DOMUtils::xPathForNode(node); } // find all elements in the SCXML namespace in one traversal -void assembleNodeSets(const std::string nsPrefix, const Node<std::string>& node, std::map<std::string, NodeSet<std::string> >& sets) { - NodeList<std::string> childs = node.getChildNodes(); - for (unsigned int i = 0; i < childs.getLength(); i++) { - if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE) +void assembleNodeSets(const std::string nsPrefix, DOMNode* node, std::map<std::string, std::list<DOMElement*> >& sets) { + DOMNodeList* childs = node->getChildNodes(); + for (unsigned int i = 0; i < childs->getLength(); i++) { + if (childs->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) continue; // std::cout << TAGNAME(childs.item(i)) << std::endl; - if (TAGNAME_CAST(childs.item(i)).find(nsPrefix) == 0) { + if (TAGNAME_CAST(childs->item(i)).find(nsPrefix) == 0) { // correct namespace, insert via localname - sets[LOCALNAME_CAST(childs.item(i))].push_back(childs.item(i)); + sets[LOCALNAME_CAST(childs->item(i))].push_back(static_cast<DOMElement*>(childs->item(i))); } - assembleNodeSets(nsPrefix, childs.item(i), sets); + assembleNodeSets(nsPrefix, childs->item(i), sets); } } +std::list<std::set<const DOMElement* > > getAllConfigurations(const DOMElement* root) { + std::list<std::set<const DOMElement* > > allConfigurations; + std::string nsPrefix = X(root->getPrefix()); + std::string localName = X(root->getLocalName()); + bool isAtomic = true; + + std::cout << *root; + + DOMNodeList* children = root->getChildNodes(); + for (size_t i = 0; i < children->getLength(); i++) { + if (children->item(i)->getNodeType() != DOMNode::ELEMENT_NODE) + continue; + DOMElement* childElem = static_cast<DOMElement*>(children->item(i)); + std::cout << *childElem; + + if (XMLString::compareIString(childElem->getTagName(), X(nsPrefix + "state")) == 0 || + XMLString::compareIString(childElem->getTagName(), X(nsPrefix + "parallel")) == 0 || + XMLString::compareIString(childElem->getTagName(), X(nsPrefix + "final")) == 0) { + // nested child state + std::list<std::set<const DOMElement*> > nestedConfigurations = getAllConfigurations(childElem); + isAtomic = false; + if (localName == "parallel" && allConfigurations.size() > 0) { + // for every nested configuration, every new nested is valid + std::list<std::set<const DOMElement*> > combinedConfigurations; + for (auto existIter = allConfigurations.begin(); existIter != allConfigurations.end(); existIter++) { + std::set<const DOMElement*> existingConfig = *existIter; + + for (auto newIter = nestedConfigurations.begin(); newIter != nestedConfigurations.end(); newIter++) { + + std::set<const DOMElement*> newConfig = *newIter; + std::set<const DOMElement*> combinedSet; + combinedSet.insert(existingConfig.begin(), existingConfig.end()); + combinedSet.insert(newConfig.begin(), newConfig.end()); + + combinedConfigurations.push_back(combinedSet); + } + } + allConfigurations = combinedConfigurations; + } else { + // just add nested configurations and this + for (auto newIter = nestedConfigurations.begin(); newIter != nestedConfigurations.end(); newIter++) { + std::set<const DOMElement*> newConfig = *newIter; + if (localName != "scxml") + newConfig.insert(root); + allConfigurations.push_back(newConfig); + } + } + } + } + + if (isAtomic) { + std::set<const DOMElement*> soleConfig; + soleConfig.insert(root); + allConfigurations.push_back(soleConfig); + } + return allConfigurations; + +} /** * Can the given states ever appear in an active configuration? */ -bool hasLegalCompletion(const NodeSet<std::string>& states) { +bool hasLegalCompletion(const std::list<DOMElement*>& states) { if (states.size() < 2) return true; // iterate every pair - for (unsigned int outer = 0; outer < states.size() - 1; outer++) { - Element<std::string> s1(states[outer]); - for (unsigned int inner = outer + 1; inner < states.size(); inner++) { - Element<std::string> s2(states[inner]); - Node<std::string> parent; + for (auto outer = states.begin(); outer != states.end(); outer++) { + DOMElement* s1 = *outer; + for (auto inner = outer; inner != states.end(); inner++) { + if (inner == outer) + continue; + + DOMElement* s2 = *inner; + DOMNode* parent; // ok to be directly ancestorally related - if (InterpreterImpl::isDescendant(s1, s2) || InterpreterImpl::isDescendant(s2, s1)) + if (DOMUtils::isDescendant(s1, s2) || DOMUtils::isDescendant(s2, s1)) goto NEXT_PAIR; // find least common ancestor - parent = s1.getParentNode(); - while(parent && parent.getNodeType() == Node_base::ELEMENT_NODE) { - if (InterpreterImpl::isDescendant(s2, parent)) { - if (InterpreterImpl::isParallel(Element<std::string>(parent))) + parent = s1->getParentNode(); + while(parent && parent->getNodeType() == DOMNode::ELEMENT_NODE) { + if (DOMUtils::isDescendant(s2, parent)) { + if (isParallel(static_cast<DOMElement*>(parent))) goto NEXT_PAIR; } - parent = parent.getParentNode(); + parent = parent->getParentNode(); } return false; @@ -100,91 +161,90 @@ std::list<InterpreterIssue> InterpreterIssue::forInterpreter(InterpreterImpl* in std::list<InterpreterIssue> issues; if (!interpreter->_scxml) { - InterpreterIssue issue("No SCXML element to be found", Node<std::string>(), InterpreterIssue::USCXML_ISSUE_FATAL); + InterpreterIssue issue("No SCXML element to be found", NULL, InterpreterIssue::USCXML_ISSUE_FATAL); issues.push_back(issue); return issues; } - std::map<std::string, Arabica::DOM::Element<std::string> > seenStates; + std::map<std::string, DOMElement* > seenStates; // get some aliases - Element<std::string>& _scxml = interpreter->_scxml; - NameSpaceInfo& _nsInfo = interpreter->_nsInfo; + DOMElement* _scxml = interpreter->_scxml; Factory* _factory = interpreter->_factory; DataModel& _dataModel = interpreter->_dataModel; + std::string xmlNSPrefix = interpreter->_xmlPrefix; + std::map<std::string, std::list<DOMElement*> > nodeSets; + assembleNodeSets(xmlNSPrefix, _scxml, nodeSets); - std::map<std::string, NodeSet<std::string> > nodeSets; - assembleNodeSets(_nsInfo.xmlNSPrefix, _scxml, nodeSets); - - NodeSet<std::string> scxmls = nodeSets["scxml"]; + std::list<DOMElement*> scxmls = nodeSets["scxml"]; scxmls.push_back(_scxml); - NodeSet<std::string> reachable = interpreter->getReachableStates(); - - NodeSet<std::string>& states = nodeSets["state"]; - NodeSet<std::string>& parallels = nodeSets["parallel"]; - NodeSet<std::string>& transitions = nodeSets["transition"]; - NodeSet<std::string>& initials = nodeSets["initial"]; - NodeSet<std::string>& finals = nodeSets["final"]; - NodeSet<std::string>& onEntries = nodeSets["onentry"]; - NodeSet<std::string>& onExits = nodeSets["onexit"]; - NodeSet<std::string>& histories = nodeSets["history"]; - - NodeSet<std::string>& raises = nodeSets["raise"]; - NodeSet<std::string>& ifs = nodeSets["if"]; - NodeSet<std::string>& elseIfs = nodeSets["elseif"]; - NodeSet<std::string>& elses = nodeSets["else"]; - NodeSet<std::string>& foreachs = nodeSets["foreach"]; - NodeSet<std::string>& logs = nodeSets["log"]; - - NodeSet<std::string>& dataModels = nodeSets["datamodel"]; - NodeSet<std::string>& datas = nodeSets["data"]; - NodeSet<std::string>& assigns = nodeSets["assign"]; - NodeSet<std::string>& doneDatas = nodeSets["donedata"]; - NodeSet<std::string>& contents = nodeSets["content"]; - NodeSet<std::string>& params = nodeSets["param"]; - NodeSet<std::string>& scripts = nodeSets["script"]; - - NodeSet<std::string>& sends = nodeSets["send"]; - NodeSet<std::string>& cancels = nodeSets["cancel"]; - NodeSet<std::string>& invokes = nodeSets["invoke"]; - NodeSet<std::string>& finalizes = nodeSets["finalize"]; - - NodeSet<std::string> allStates; - allStates.push_back(states); - allStates.push_back(parallels); - allStates.push_back(histories); - allStates.push_back(finals); - - NodeSet<std::string> allExecContents; - allExecContents.push_back(raises); - allExecContents.push_back(ifs); - allExecContents.push_back(elseIfs); - allExecContents.push_back(elses); - allExecContents.push_back(foreachs); - allExecContents.push_back(logs); - allExecContents.push_back(sends); - allExecContents.push_back(assigns); - allExecContents.push_back(scripts); - allExecContents.push_back(cancels); - - NodeSet<std::string> allElements; - allElements.push_back(scxmls); - allElements.push_back(allStates); - allElements.push_back(allExecContents); - allElements.push_back(transitions); - allElements.push_back(initials); - allElements.push_back(onEntries); - allElements.push_back(onExits); - allElements.push_back(dataModels); - allElements.push_back(datas); - allElements.push_back(doneDatas); - allElements.push_back(contents); - allElements.push_back(params); - allElements.push_back(invokes); - allElements.push_back(finalizes); + std::list<xercesc::DOMElement*> reachable = getReachableStates(_scxml); + + std::list<DOMElement*>& states = nodeSets["state"]; + std::list<DOMElement*>& parallels = nodeSets["parallel"]; + std::list<DOMElement*>& transitions = nodeSets["transition"]; + std::list<DOMElement*>& initials = nodeSets["initial"]; + std::list<DOMElement*>& finals = nodeSets["final"]; + std::list<DOMElement*>& onEntries = nodeSets["onentry"]; + std::list<DOMElement*>& onExits = nodeSets["onexit"]; + std::list<DOMElement*>& histories = nodeSets["history"]; + + std::list<DOMElement*>& raises = nodeSets["raise"]; + std::list<DOMElement*>& ifs = nodeSets["if"]; + std::list<DOMElement*>& elseIfs = nodeSets["elseif"]; + std::list<DOMElement*>& elses = nodeSets["else"]; + std::list<DOMElement*>& foreachs = nodeSets["foreach"]; + std::list<DOMElement*>& logs = nodeSets["log"]; + + std::list<DOMElement*>& dataModels = nodeSets["datamodel"]; + std::list<DOMElement*>& datas = nodeSets["data"]; + std::list<DOMElement*>& assigns = nodeSets["assign"]; + std::list<DOMElement*>& doneDatas = nodeSets["donedata"]; + std::list<DOMElement*>& contents = nodeSets["content"]; + std::list<DOMElement*>& params = nodeSets["param"]; + std::list<DOMElement*>& scripts = nodeSets["script"]; + + std::list<DOMElement*>& sends = nodeSets["send"]; + std::list<DOMElement*>& cancels = nodeSets["cancel"]; + std::list<DOMElement*>& invokes = nodeSets["invoke"]; + std::list<DOMElement*>& finalizes = nodeSets["finalize"]; + + std::list<DOMElement*> allStates; + allStates.insert(allStates.end(), states.begin(), states.end()); + allStates.insert(allStates.end(), parallels.begin(), parallels.end()); + allStates.insert(allStates.end(), histories.begin(), histories.end()); + allStates.insert(allStates.end(), finals.begin(), finals.end()); + + std::list<DOMElement*> allExecContents; + allExecContents.insert(allExecContents.end(), raises.begin(), raises.end()); + allExecContents.insert(allExecContents.end(), ifs.begin(), ifs.end()); + allExecContents.insert(allExecContents.end(), elseIfs.begin(), elseIfs.end()); + allExecContents.insert(allExecContents.end(), elses.begin(), elses.end()); + allExecContents.insert(allExecContents.end(), foreachs.begin(), foreachs.end()); + allExecContents.insert(allExecContents.end(), logs.begin(), logs.end()); + allExecContents.insert(allExecContents.end(), sends.begin(), sends.end()); + allExecContents.insert(allExecContents.end(), assigns.begin(), assigns.end()); + allExecContents.insert(allExecContents.end(), scripts.begin(), scripts.end()); + allExecContents.insert(allExecContents.end(), cancels.begin(), cancels.end()); + + std::list<DOMElement*> allElements; + allElements.insert(allElements.end(), scxmls.begin(), scxmls.end()); + allElements.insert(allElements.end(), allStates.begin(), allStates.end()); + allElements.insert(allElements.end(), allExecContents.begin(), allExecContents.end()); + allElements.insert(allElements.end(), transitions.begin(), transitions.end()); + allElements.insert(allElements.end(), initials.begin(), initials.end()); + allElements.insert(allElements.end(), onEntries.begin(), onEntries.end()); + allElements.insert(allElements.end(), onExits.begin(), onExits.end()); + allElements.insert(allElements.end(), dataModels.begin(), dataModels.end()); + allElements.insert(allElements.end(), datas.begin(), datas.end()); + allElements.insert(allElements.end(), doneDatas.begin(), doneDatas.end()); + allElements.insert(allElements.end(), contents.begin(), contents.end()); + allElements.insert(allElements.end(), params.begin(), params.end()); + allElements.insert(allElements.end(), invokes.begin(), invokes.end()); + allElements.insert(allElements.end(), finalizes.begin(), finalizes.end()); std::set<std::string> execContentSet; @@ -278,10 +338,10 @@ std::list<InterpreterIssue> InterpreterIssue::forInterpreter(InterpreterImpl* in } - for (size_t i = 0; i < allStates.size(); i++) { - Element<std::string> state = Element<std::string>(allStates[i]); + for (auto stateIter = allStates.begin(); stateIter != allStates.end(); stateIter++) { + DOMElement* state = static_cast<DOMElement*>(*stateIter); - if (InterpreterImpl::isMember(state, finals) && !HAS_ATTR(state, "id")) // id is not required for finals + if (DOMUtils::isMember(state, finals) && !HAS_ATTR(state, "id")) // id is not required for finals continue; // check for existance of id attribute - this not actually required! @@ -299,13 +359,13 @@ std::list<InterpreterIssue> InterpreterIssue::forInterpreter(InterpreterImpl* in // check for valid transition with history states if (LOCALNAME(state) == "history") { - NodeSet<std::string> transitions = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "transition", state, false); + std::list<DOMElement*> transitions = DOMUtils::filterChildElements(XML_PREFIX(state).str() + "transition", state, false); if (transitions.size() > 1) { issues.push_back(InterpreterIssue("History pseudo-state with id '" + stateId + "' has multiple transitions", state, InterpreterIssue::USCXML_ISSUE_FATAL)); } else if (transitions.size() == 0) { issues.push_back(InterpreterIssue("History pseudo-state with id '" + stateId + "' has no default transition", state, InterpreterIssue::USCXML_ISSUE_FATAL)); } else { - Element<std::string> transition = Element<std::string>(transitions[0]); + DOMElement* transition = static_cast<DOMElement*>(transitions.front()); if (HAS_ATTR(transition, "cond")) { issues.push_back(InterpreterIssue("Transition in history pseudo-state '" + stateId + "' must not have a condition", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); } @@ -315,16 +375,16 @@ std::list<InterpreterIssue> InterpreterIssue::forInterpreter(InterpreterImpl* in if (!HAS_ATTR(transition, "target")) { issues.push_back(InterpreterIssue("Transition in history pseudo-state '" + stateId + "' has no target", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); } else { - NodeSet<std::string> targetStates = interpreter->getTargetStates(transition); - for (size_t j = 0; j < targetStates.size(); j++) { - Element<std::string> target = Element<std::string>(targetStates[j]); + std::list<DOMElement*> targetStates = getTargetStates(transition, _scxml); + for (auto tIter = targetStates.begin(); tIter != targetStates.end(); tIter++) { + DOMElement* target = *tIter; if (HAS_ATTR(state, "type") && ATTR(state, "type") == "deep") { - if (!InterpreterImpl::isDescendant(target, state.getParentNode())) { - issues.push_back(InterpreterIssue("Transition in deep history pseudo-state '" + stateId + "' has invalid target state '" + ATTR(target, "id") + "'", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); + if (!DOMUtils::isDescendant(target, state->getParentNode())) { + issues.push_back(InterpreterIssue("Transition in deep history pseudo-state '" + stateId + "' has illegal target state '" + ATTR(target, "id") + "'", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); } } else { - if (target.getParentNode() != state.getParentNode()) { - issues.push_back(InterpreterIssue("Transition in shallow history pseudo-state '" + stateId + "' has invalid target state '" + ATTR(target, "id") + "'", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); + if (target->getParentNode() != state->getParentNode()) { + issues.push_back(InterpreterIssue("Transition in shallow history pseudo-state '" + stateId + "' has illegal target state '" + ATTR(target, "id") + "'", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); } } } @@ -333,7 +393,7 @@ std::list<InterpreterIssue> InterpreterIssue::forInterpreter(InterpreterImpl* in } // check whether state is reachable - if (!InterpreterImpl::isMember(state, reachable) && !InterpreterImpl::isInEmbeddedDocument(state)) { + if (!DOMUtils::isMember(state, reachable) && !isInEmbeddedDocument(state)) { issues.push_back(InterpreterIssue("State with id '" + stateId + "' is unreachable", state, InterpreterIssue::USCXML_ISSUE_FATAL)); } @@ -345,8 +405,8 @@ std::list<InterpreterIssue> InterpreterIssue::forInterpreter(InterpreterImpl* in seenStates[ATTR(state, "id")] = state; } - for (size_t i = 0; i < transitions.size(); i++) { - Element<std::string> transition = Element<std::string>(transitions[i]); + for (auto tIter = transitions.begin(); tIter != transitions.end(); tIter++) { + DOMElement* transition = *tIter; // check for valid target if (HAS_ATTR(transition, "target")) { @@ -365,16 +425,14 @@ std::list<InterpreterIssue> InterpreterIssue::forInterpreter(InterpreterImpl* in } // check for redundancy of transition - for (size_t i = 0; i < allStates.size(); i++) { - Element<std::string> state = Element<std::string>(allStates[i]); - NodeSet<std::string> transitions = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "transition", state, false); - - transitions.to_document_order(); + for (auto stateIter = allStates.begin(); stateIter != allStates.end(); stateIter++) { + DOMElement* state = *stateIter; + std::list<DOMElement*> transitions = DOMUtils::filterChildElements(XML_PREFIX(state).str() + "transition", state, false); - for (size_t j = 1; j < transitions.size(); j++) { - Element<std::string> transition = Element<std::string>(transitions[j]); - for (size_t k = 0; k < j; k++) { - Element<std::string> earlierTransition = Element<std::string>(transitions[k]); + for (auto tIter = transitions.begin(); tIter != transitions.end(); tIter++) { + DOMElement* transition = *tIter; + for (auto t2Iter = transitions.begin(); t2Iter != tIter; t2Iter++) { + DOMElement* earlierTransition = *t2Iter; // will the earlier transition always be enabled when the later is? if (!HAS_ATTR(earlierTransition, "cond")) { @@ -410,16 +468,18 @@ NEXT_TRANSITION: // check for useless history elements { - for (size_t i = 0; i < histories.size(); i++) { - Element<std::string> history = Element<std::string>(histories[i]); - if (!history.getParentNode() || history.getParentNode().getNodeType() != Node_base::ELEMENT_NODE) + for (auto histIter = histories.begin(); histIter != histories.end(); histIter++) { + DOMElement* history = *histIter; + + if (!history->getParentNode() || history->getParentNode()->getNodeType() != DOMNode::ELEMENT_NODE) continue; // syntax check will have catched this - Element<std::string> parent(history.getParentNode()); - if (InterpreterImpl::isAtomic(parent)) { + + DOMElement* parent = static_cast<DOMElement*>(history->getParentNode()); + if (isAtomic(parent)) { issues.push_back(InterpreterIssue("Useless history '" + ATTR(history, "id") + "' in atomic state", history, InterpreterIssue::USCXML_ISSUE_INFO)); continue; } - std::list<std::set<Arabica::DOM::Element<std::string> > > configs = Complexity::getAllConfigurations(parent); + std::list<std::set<const DOMElement* > > configs = getAllConfigurations(parent); if (configs.size() <= 1) { issues.push_back(InterpreterIssue("Useless history '" + ATTR(history, "id") + "' in state with single legal configuration", history, InterpreterIssue::USCXML_ISSUE_INFO)); continue; @@ -429,19 +489,24 @@ NEXT_TRANSITION: // check for valid initial attribute { - NodeSet<std::string> withInitialAttr; - withInitialAttr.push_back(allStates); + std::list<DOMElement*> withInitialAttr; + withInitialAttr.insert(withInitialAttr.end(), allStates.begin(), allStates.end()); withInitialAttr.push_back(_scxml); - for (size_t i = 0; i < withInitialAttr.size(); i++) { - Element<std::string> state = Element<std::string>(withInitialAttr[i]); + for (auto stateIter = withInitialAttr.begin(); stateIter != withInitialAttr.end(); stateIter++) { + DOMElement* state = *stateIter; if (HAS_ATTR(state, "initial")) { - NodeSet<std::string> childs; - childs.push_back(DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "state", state, true)); - childs.push_back(DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "parallel", state, true)); - childs.push_back(DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "final", state, true)); - childs.push_back(DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "history", state, true)); + std::list<DOMElement*> childs; + std::list<DOMElement*> tmp; + tmp = DOMUtils::filterChildElements(XML_PREFIX(state).str() + "state", state, true); + childs.insert(childs.end(), tmp.begin(), tmp.end()); + tmp = DOMUtils::filterChildElements(XML_PREFIX(state).str() + "parallel", state, true); + childs.insert(childs.end(), tmp.begin(), tmp.end()); + tmp = DOMUtils::filterChildElements(XML_PREFIX(state).str() + "final", state, true); + childs.insert(childs.end(), tmp.begin(), tmp.end()); + tmp = DOMUtils::filterChildElements(XML_PREFIX(state).str() + "history", state, true); + childs.insert(childs.end(), tmp.begin(), tmp.end()); std::list<std::string> intials = tokenize(ATTR(state, "initial")); for (std::list<std::string>::iterator initIter = intials.begin(); initIter != intials.end(); initIter++) { @@ -450,7 +515,7 @@ NEXT_TRANSITION: continue; } // value of the 'initial' attribute [..] must be descendants of the containing <state> or <parallel> element - if (!InterpreterImpl::isMember(seenStates[*initIter], childs)) { + if (!DOMUtils::isMember(seenStates[*initIter], childs)) { issues.push_back(InterpreterIssue("Initial attribute references non-child state '" + *initIter + "'", state, InterpreterIssue::USCXML_ISSUE_FATAL)); } } @@ -460,37 +525,37 @@ NEXT_TRANSITION: // check for legal configuration of target sets { - std::map<Element<std::string>, std::string > targetIdSets; - for (size_t i = 0; i < transitions.size(); i++) { - Element<std::string> transition = Element<std::string>(transitions[i]); + std::map<DOMElement*, std::string > targetIdSets; + for (auto iter = transitions.begin(); iter != transitions.end(); iter++) { + DOMElement* transition = *iter; if (HAS_ATTR(transition, "target")) { targetIdSets[transition] = ATTR(transition, "target"); } } - for (size_t i = 0; i < initials.size(); i++) { - Element<std::string> initial = Element<std::string>(initials[i]); + for (auto iter = initials.begin(); iter != initials.end(); iter++) { + DOMElement* initial = *iter; if (HAS_ATTR(initial, "target")) { targetIdSets[initial] = ATTR(initial, "target"); } } - for (size_t i = 0; i < allStates.size(); i++) { - Element<std::string> state = Element<std::string>(allStates[i]); + for (auto iter = allStates.begin(); iter != allStates.end(); iter++) { + DOMElement* state = *iter; if (HAS_ATTR(state, "initial")) { targetIdSets[state] = ATTR(state, "initial"); } } - for (std::map<Element<std::string>, std::string >::iterator setIter = targetIdSets.begin(); + for (auto setIter = targetIdSets.begin(); setIter != targetIdSets.end(); setIter++) { - NodeSet<std::string> targets; + std::list<DOMElement*> targets; std::list<std::string> targetIds = tokenize(setIter->second); - for (std::list<std::string>::iterator tgtIter = targetIds.begin(); tgtIter != targetIds.end(); tgtIter++) { + for (auto tgtIter = targetIds.begin(); tgtIter != targetIds.end(); tgtIter++) { if (seenStates.find(*tgtIter) == seenStates.end()) goto NEXT_SET; targets.push_back(seenStates[*tgtIter]); @@ -505,21 +570,21 @@ NEXT_SET: // check for valid initial transition { - NodeSet<std::string> initTrans; -// initTrans.push_back(DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "transition", histories, true)); + std::list<DOMElement*> initTrans; + + for (auto iter = initials.begin(); iter != initials.end(); iter++) { + DOMElement* initial = *iter; - for (size_t i = 0; i < initials.size(); i++) { - Element<std::string> initial = Element<std::string>(initials[i]); - NodeSet<std::string> initTransitions = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "transition", initial, true); + std::list<DOMElement*> initTransitions = DOMUtils::filterChildElements(XML_PREFIX(initial).str() + "transition", initial, true); if (initTransitions.size() != 1) { issues.push_back(InterpreterIssue("Initial element must define exactly one transition", initial, InterpreterIssue::USCXML_ISSUE_FATAL)); } - initTrans.push_back(initTransitions); + initTrans.insert(initTrans.end(), initTransitions.begin(), initTransitions.end()); } - for (size_t i = 0; i < initTrans.size(); i++) { - Element<std::string> transition = Element<std::string>(initTrans[i]); + for (auto iter = initTrans.begin(); iter != initTrans.end(); iter++) { + DOMElement* transition = *iter; /* In a conformant SCXML document, this transition must not contain 'cond' or 'event' attributes, and must specify a non-null 'target' * whose value is a valid state specification consisting solely of descendants of the containing state @@ -532,22 +597,29 @@ NEXT_SET: issues.push_back(InterpreterIssue("Initial transition cannot be eventful", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); } - if (!transition.getParentNode() || !transition.getParentNode().getParentNode() || transition.getParentNode().getParentNode().getNodeType() != Node_base::ELEMENT_NODE) + if (!transition->getParentNode() || + !transition->getParentNode()->getParentNode() || + transition->getParentNode()->getParentNode()->getNodeType() != DOMNode::ELEMENT_NODE) continue; // syntax will catch this one - Element<std::string> state(transition.getParentNode().getParentNode()); - if (!InterpreterImpl::isState(state)) + DOMElement* state = static_cast<DOMElement*>(transition->getParentNode()->getParentNode()); + if (!isState(state)) continue; // syntax will catch this one - NodeSet<std::string> childs; - childs.push_back(DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "state", state, true)); - childs.push_back(DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "parallel", state, true)); - childs.push_back(DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "final", state, true)); - childs.push_back(DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "history", state, true)); + std::list<DOMElement*> childs; + std::list<DOMElement*> tmp; + tmp = DOMUtils::filterChildElements(XML_PREFIX(state).str() + "state", state, true); + childs.insert(childs.end(), tmp.begin(), tmp.end()); + tmp = DOMUtils::filterChildElements(XML_PREFIX(state).str() + "parallel", state, true); + childs.insert(childs.end(), tmp.begin(), tmp.end()); + tmp = DOMUtils::filterChildElements(XML_PREFIX(state).str() + "final", state, true); + childs.insert(childs.end(), tmp.begin(), tmp.end()); + tmp = DOMUtils::filterChildElements(XML_PREFIX(state).str() + "history", state, true); + childs.insert(childs.end(), tmp.begin(), tmp.end()); std::list<std::string> intials = tokenize(ATTR(transition, "target")); for (std::list<std::string>::iterator initIter = intials.begin(); initIter != intials.end(); initIter++) { // the 'target' of a <transition> inside an <initial> or <history> element: all the states must be descendants of the containing <state> or <parallel> element - if (!InterpreterImpl::isMember(seenStates[*initIter], childs)) { + if (!DOMUtils::isMember(seenStates[*initIter], childs)) { issues.push_back(InterpreterIssue("Target of initial transition references non-child state '" + *initIter + "'", transition, InterpreterIssue::USCXML_ISSUE_FATAL)); } } @@ -557,8 +629,8 @@ NEXT_SET: // check that all invokers exists { - for (size_t i = 0; i < invokes.size(); i++) { - Element<std::string> invoke = Element<std::string>(invokes[i]); + for (auto iter = invokes.begin(); iter != invokes.end(); iter++) { + DOMElement* invoke = *iter; if (HAS_ATTR(invoke, "type") && !_factory->hasInvoker(ATTR(invoke, "type"))) { // unknown at factory - adhoc extension? if (HAS_ATTR(invoke, "id") && interpreter->_invokers.find(ATTR(invoke, "id")) != interpreter->_invokers.end()) @@ -580,10 +652,10 @@ NEXT_SET: // check that all io processors exists { - for (size_t i = 0; i < sends.size(); i++) { - Element<std::string> send = Element<std::string>(sends[i]); + for (auto iter = sends.begin(); iter != sends.end(); iter++) { + DOMElement* send = *iter; if (HAS_ATTR(send, "type") && !_factory->hasIOProcessor(ATTR(send, "type"))) { - if (interpreter->_ioProcessors.find(ATTR(send, "type")) != interpreter->_ioProcessors.end()) + if (interpreter->_ioProcs.find(ATTR(send, "type")) != interpreter->_ioProcs.end()) continue; // not an issue, available ad-hoc issues.push_back(InterpreterIssue("Send to unknown IO Processor '" + ATTR(send, "type") + "'", send, InterpreterIssue::USCXML_ISSUE_FATAL)); @@ -594,23 +666,24 @@ NEXT_SET: // check that all custom executable content is known { - NodeSet<std::string> allExecContentContainers; - allExecContentContainers.push_back(onEntries); - allExecContentContainers.push_back(onExits); - allExecContentContainers.push_back(transitions); - allExecContentContainers.push_back(finalizes); - - for (size_t i = 0; i < allExecContentContainers.size(); i++) { - Element<std::string> block = Element<std::string>(allExecContentContainers[i]); - NodeSet<std::string> execContents = DOMUtils::filterChildType(Node_base::ELEMENT_NODE, block); - for (size_t j = 0; j < execContents.size(); j++) { - Element<std::string> execContent = Element<std::string>(execContents[j]); + std::list<DOMElement*> allExecContentContainers; + allExecContentContainers.insert(allExecContentContainers.end(), onEntries.begin(), onEntries.end()); + allExecContentContainers.insert(allExecContentContainers.end(), onExits.begin(), onExits.end()); + allExecContentContainers.insert(allExecContentContainers.end(), transitions.begin(), transitions.end()); + allExecContentContainers.insert(allExecContentContainers.end(), finalizes.begin(), finalizes.end()); + + for (auto bIter = allExecContentContainers.begin(); bIter != allExecContentContainers.end(); bIter++) { + DOMElement* block = *bIter; + std::list<DOMNode*> execContents = DOMUtils::filterChildType(DOMNode::ELEMENT_NODE, block); + for (auto ecIter = execContents.begin(); ecIter != execContents.end(); ecIter++) { + DOMElement* execContent = static_cast<DOMElement*>(*ecIter); // SCXML specific executable content, always available - if (InterpreterImpl::isMember(execContent, allExecContents)) { + if (DOMUtils::isMember(execContent, allExecContents)) { continue; } - if (!_factory->hasExecutableContent(execContent.getLocalName(), execContent.getNamespaceURI())) { - issues.push_back(InterpreterIssue("Executable content element '" + execContent.getLocalName() + "' in namespace '" + execContent.getNamespaceURI() + "' unknown", execContent, InterpreterIssue::USCXML_ISSUE_FATAL)); + + if (!_factory->hasExecutableContent(X(execContent->getLocalName()), X(execContent->getNamespaceURI()))) { + issues.push_back(InterpreterIssue("Executable content element '" + (std::string)X(execContent->getLocalName()) + "' in namespace '" + (std::string)X(execContent->getNamespaceURI()) + "' unknown", execContent, InterpreterIssue::USCXML_ISSUE_FATAL)); continue; } } @@ -619,8 +692,8 @@ NEXT_SET: // check that all SCXML elements have valid parents and required attributes { - for (size_t i = 0; i < allElements.size(); i++) { - Element<std::string> element = Element<std::string>(allElements[i]); + for (auto iter = allElements.begin(); iter != allElements.end(); iter++) { + DOMElement* element = *iter; std::string localName = LOCALNAME(element); if (reqAttr.find(localName) != reqAttr.end()) { @@ -638,12 +711,12 @@ NEXT_SET: if (localName == "scxml") // can be anywhere: <assign>, <content>, <other:embed> continue; - if (!element.getParentNode() || element.getParentNode().getNodeType() != Node_base::ELEMENT_NODE) { + if (!element->getParentNode() || element->getParentNode()->getNodeType() != DOMNode::ELEMENT_NODE) { issues.push_back(InterpreterIssue("Parent of " + localName + " is no element", element, InterpreterIssue::USCXML_ISSUE_WARNING)); continue; } - Element<std::string> parent = Element<std::string>(element.getParentNode()); + DOMElement* parent = static_cast<DOMElement*>(element->getParentNode()); std::string parentName = LOCALNAME(parent); if (validParents[localName].find(parentName) == validParents[localName].end()) { @@ -655,55 +728,55 @@ NEXT_SET: // check attribute constraints { - for (size_t i = 0; i < initials.size(); i++) { - Element<std::string> initial = Element<std::string>(initials[i]); - if (initial.getParentNode() && initial.getParentNode().getNodeType() == Node_base::ELEMENT_NODE) { - Element<std::string> state(initial.getParentNode()); + for (auto iter = initials.begin(); iter != initials.end(); iter++) { + DOMElement* initial = *iter; + if (initial->getParentNode() && initial->getParentNode()->getNodeType() == DOMNode::ELEMENT_NODE) { + DOMElement* state = static_cast<DOMElement*>(initial->getParentNode()); if (HAS_ATTR(state, "initial")) { issues.push_back(InterpreterIssue("State with initial attribute cannot have <initial> child ", state, InterpreterIssue::USCXML_ISSUE_WARNING)); } - if (InterpreterImpl::isAtomic(state)) { + if (isAtomic(state)) { issues.push_back(InterpreterIssue("Atomic state cannot have <initial> child ", state, InterpreterIssue::USCXML_ISSUE_WARNING)); } } } - for (size_t i = 0; i < allStates.size(); i++) { - Element<std::string> state = Element<std::string>(allStates[i]); - if (InterpreterImpl::isAtomic(state) && HAS_ATTR(state, "initial")) { + for (auto iter = allStates.begin(); iter != allStates.end(); iter++) { + DOMElement* state = *iter; + if (isAtomic(state) && HAS_ATTR(state, "initial")) { issues.push_back(InterpreterIssue("Atomic state cannot have initial attribute ", state, InterpreterIssue::USCXML_ISSUE_WARNING)); } } - for (size_t i = 0; i < assigns.size(); i++) { - Element<std::string> assign = Element<std::string>(assigns[i]); - if (HAS_ATTR(assign, "expr") && assign.getChildNodes().getLength() > 0) { + for (auto iter = assigns.begin(); iter != assigns.end(); iter++) { + DOMElement* assign = *iter; + if (HAS_ATTR(assign, "expr") && assign->getChildNodes()->getLength() > 0) { issues.push_back(InterpreterIssue("Assign element cannot have expr attribute and children", assign, InterpreterIssue::USCXML_ISSUE_WARNING)); } } - for (size_t i = 0; i < contents.size(); i++) { - Element<std::string> content = Element<std::string>(contents[i]); - if (HAS_ATTR(content, "expr") && content.getChildNodes().getLength() > 0) { + for (auto iter = contents.begin(); iter != contents.end(); iter++) { + DOMElement* content = *iter; + if (HAS_ATTR(content, "expr") && content->getChildNodes()->getLength() > 0) { issues.push_back(InterpreterIssue("Content element cannot have expr attribute and children", content, InterpreterIssue::USCXML_ISSUE_WARNING)); } } - for (size_t i = 0; i < params.size(); i++) { - Element<std::string> param = Element<std::string>(params[i]); + for (auto iter = params.begin(); iter != params.end(); iter++) { + DOMElement* param = *iter; if (HAS_ATTR(param, "expr") && HAS_ATTR(param, "location")) { issues.push_back(InterpreterIssue("Param element cannot have both expr and location attribute", param, InterpreterIssue::USCXML_ISSUE_WARNING)); } } - for (size_t i = 0; i < scripts.size(); i++) { - Element<std::string> script = Element<std::string>(scripts[i]); - if (HAS_ATTR(script, "src") && script.getChildNodes().getLength() > 0) { + for (auto iter = scripts.begin(); iter != scripts.end(); iter++) { + DOMElement* script = *iter; + if (HAS_ATTR(script, "src") && script->getChildNodes()->getLength() > 0) { issues.push_back(InterpreterIssue("Script element cannot have src attribute and children", script, InterpreterIssue::USCXML_ISSUE_WARNING)); } } - for (size_t i = 0; i < sends.size(); i++) { - Element<std::string> send = Element<std::string>(sends[i]); + for (auto iter = sends.begin(); iter != sends.end(); iter++) { + DOMElement* send = *iter; if (HAS_ATTR(send, "event") && HAS_ATTR(send, "eventexpr")) { issues.push_back(InterpreterIssue("Send element cannot have both event and eventexpr attribute", send, InterpreterIssue::USCXML_ISSUE_WARNING)); } @@ -723,8 +796,8 @@ NEXT_SET: issues.push_back(InterpreterIssue("Send element cannot have delay with target _internal", send, InterpreterIssue::USCXML_ISSUE_WARNING)); } - NodeSet<std::string> contentChilds = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "content", send, false); - NodeSet<std::string> paramChilds = DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "param", send, false); + std::list<DOMElement*> contentChilds = DOMUtils::filterChildElements(XML_PREFIX(send).str() + "content", send, false); + std::list<DOMElement*> paramChilds = DOMUtils::filterChildElements(XML_PREFIX(send).str() + "param", send, false); if (HAS_ATTR(send, "namelist") && contentChilds.size() > 0) { issues.push_back(InterpreterIssue("Send element cannot have namelist attribute and content child", send, InterpreterIssue::USCXML_ISSUE_WARNING)); @@ -735,15 +808,15 @@ NEXT_SET: } } - for (size_t i = 0; i < cancels.size(); i++) { - Element<std::string> cancel = Element<std::string>(cancels[i]); + for (auto iter = cancels.begin(); iter != cancels.end(); iter++) { + DOMElement* cancel = *iter; if (HAS_ATTR(cancel, "sendid") && HAS_ATTR(cancel, "sendidexpr")) { issues.push_back(InterpreterIssue("Cancel element cannot have both sendid and eventexpr sendidexpr", cancel, InterpreterIssue::USCXML_ISSUE_WARNING)); } } - for (size_t i = 0; i < invokes.size(); i++) { - Element<std::string> invoke = Element<std::string>(invokes[i]); + for (auto iter = invokes.begin(); iter != invokes.end(); iter++) { + DOMElement* invoke = *iter; if (HAS_ATTR(invoke, "type") && HAS_ATTR(invoke, "typeexpr")) { issues.push_back(InterpreterIssue("Invoke element cannot have both type and typeexpr attribute", invoke, InterpreterIssue::USCXML_ISSUE_WARNING)); } @@ -753,18 +826,18 @@ NEXT_SET: if (HAS_ATTR(invoke, "id") && HAS_ATTR(invoke, "idlocation")) { issues.push_back(InterpreterIssue("Invoke element cannot have both id and idlocation attribute", invoke, InterpreterIssue::USCXML_ISSUE_WARNING)); } - if (HAS_ATTR(invoke, "namelist") && DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "param", invoke, false).size() > 0) { + if (HAS_ATTR(invoke, "namelist") && DOMUtils::filterChildElements(XML_PREFIX(invoke).str() + "param", invoke, false).size() > 0) { issues.push_back(InterpreterIssue("Invoke element cannot have namelist attribute and param child", invoke, InterpreterIssue::USCXML_ISSUE_WARNING)); } - if (HAS_ATTR(invoke, "src") && DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "content", invoke, false).size() > 0) { + if (HAS_ATTR(invoke, "src") && DOMUtils::filterChildElements(XML_PREFIX(invoke).str() + "content", invoke, false).size() > 0) { issues.push_back(InterpreterIssue("Invoke element cannot have src attribute and content child", invoke, InterpreterIssue::USCXML_ISSUE_WARNING)); } } - for (size_t i = 0; i < doneDatas.size(); i++) { - Element<std::string> donedata = Element<std::string>(doneDatas[i]); - if (DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "content", donedata, false).size() > 0 && - DOMUtils::filterChildElements(_nsInfo.xmlNSPrefix + "param", donedata, false).size() > 0) { + for (auto iter = doneDatas.begin(); iter != doneDatas.end(); iter++) { + DOMElement* donedata = *iter; + if (DOMUtils::filterChildElements(XML_PREFIX(donedata).str() + "content", donedata, false).size() > 0 && + DOMUtils::filterChildElements(XML_PREFIX(donedata).str() + "param", donedata, false).size() > 0) { issues.push_back(InterpreterIssue("Donedata element cannot have param child and content child", donedata, InterpreterIssue::USCXML_ISSUE_WARNING)); } @@ -798,15 +871,15 @@ NEXT_SET: // test all scripts for valid syntax { - for (size_t i = 0; i < scripts.size(); i++) { - Element<std::string> script = Element<std::string>(scripts[i]); + for (auto iter = scripts.begin(); iter != scripts.end(); iter++) { + DOMElement* script = *iter; - if (script.hasChildNodes()) { + if (script->hasChildNodes()) { // search for the text node with the actual script std::string scriptContent; - for (Node<std::string> child = script.getFirstChild(); child; child = child.getNextSibling()) { - if (child.getNodeType() == Node_base::TEXT_NODE || child.getNodeType() == Node_base::CDATA_SECTION_NODE) - scriptContent += child.getNodeValue(); + for (DOMNode* child = script->getFirstChild(); child; child = child->getNextSibling()) { + if (child->getNodeType() == DOMNode::TEXT_NODE || child->getNodeType() == DOMNode::CDATA_SECTION_NODE) + scriptContent += (std::string)X(child->getNodeValue()); } if (!_dataModel.isValidSyntax(scriptContent)) { @@ -818,13 +891,13 @@ NEXT_SET: // test the various attributes with datamodel expressions for valid syntax { - NodeSet<std::string> withCondAttrs; - withCondAttrs.push_back(transitions); - withCondAttrs.push_back(ifs); - withCondAttrs.push_back(elseIfs); + std::list<DOMElement*> withCondAttrs; + withCondAttrs.insert(withCondAttrs.end(), transitions.begin(), transitions.end()); + withCondAttrs.insert(withCondAttrs.end(), ifs.begin(), ifs.end()); + withCondAttrs.insert(withCondAttrs.end(), elseIfs.begin(), elseIfs.end()); - for (size_t i = 0; i < withCondAttrs.size(); i++) { - Element<std::string> condAttr = Element<std::string>(withCondAttrs[i]); + for (auto iter = withCondAttrs.begin(); iter != withCondAttrs.end(); iter++) { + DOMElement* condAttr = *iter; if (HAS_ATTR(condAttr, "cond")) { if (!_dataModel.isValidSyntax(ATTR(condAttr, "cond"))) { issues.push_back(InterpreterIssue("Syntax error in cond attribute", condAttr, InterpreterIssue::USCXML_ISSUE_WARNING)); @@ -835,17 +908,17 @@ NEXT_SET: } { - NodeSet<std::string> withExprAttrs; - withExprAttrs.push_back(logs); - withExprAttrs.push_back(datas); - withExprAttrs.push_back(assigns); - withExprAttrs.push_back(contents); - withExprAttrs.push_back(params); - - for (size_t i = 0; i < withExprAttrs.size(); i++) { - Element<std::string> withExprAttr = Element<std::string>(withExprAttrs[i]); + std::list<DOMElement*> withExprAttrs; + withExprAttrs.insert(withExprAttrs.end(), logs.begin(), logs.end()); + withExprAttrs.insert(withExprAttrs.end(), datas.begin(), datas.end()); + withExprAttrs.insert(withExprAttrs.end(), assigns.begin(), assigns.end()); + withExprAttrs.insert(withExprAttrs.end(), contents.begin(), contents.end()); + withExprAttrs.insert(withExprAttrs.end(), params.begin(), params.end()); + + for (auto iter = withExprAttrs.begin(); iter != withExprAttrs.end(); iter++) { + DOMElement* withExprAttr = *iter; if (HAS_ATTR(withExprAttr, "expr")) { - if (InterpreterImpl::isMember(withExprAttr, datas) || InterpreterImpl::isMember(withExprAttr, assigns)) { + if (DOMUtils::isMember(withExprAttr, datas) || DOMUtils::isMember(withExprAttr, assigns)) { if (!_dataModel.isValidSyntax("foo = " + ATTR(withExprAttr, "expr"))) { // TODO: this is ECMAScripty! issues.push_back(InterpreterIssue("Syntax error in expr attribute", withExprAttr, InterpreterIssue::USCXML_ISSUE_WARNING)); continue; @@ -861,8 +934,8 @@ NEXT_SET: } { - for (size_t i = 0; i < foreachs.size(); i++) { - Element<std::string> foreach = Element<std::string>(foreachs[i]); + for (auto iter = foreachs.begin(); iter != foreachs.end(); iter++) { + DOMElement* foreach = *iter; if (HAS_ATTR(foreach, "array")) { if (!_dataModel.isValidSyntax(ATTR(foreach, "array"))) { issues.push_back(InterpreterIssue("Syntax error in array attribute", foreach, InterpreterIssue::USCXML_ISSUE_WARNING)); @@ -882,8 +955,8 @@ NEXT_SET: } { - for (size_t i = 0; i < sends.size(); i++) { - Element<std::string> send = Element<std::string>(sends[i]); + for (auto iter = sends.begin(); iter != sends.end(); iter++) { + DOMElement* send = *iter; if (HAS_ATTR(send, "eventexpr")) { if (!_dataModel.isValidSyntax(ATTR(send, "eventexpr"))) { issues.push_back(InterpreterIssue("Syntax error in eventexpr attribute", send, InterpreterIssue::USCXML_ISSUE_WARNING)); @@ -914,8 +987,8 @@ NEXT_SET: } { - for (size_t i = 0; i < invokes.size(); i++) { - Element<std::string> invoke = Element<std::string>(invokes[i]); + for (auto iter = invokes.begin(); iter != invokes.end(); iter++) { + DOMElement* invoke = *iter; if (HAS_ATTR(invoke, "typeexpr")) { if (!_dataModel.isValidSyntax(ATTR(invoke, "typeexpr"))) { issues.push_back(InterpreterIssue("Syntax error in typeexpr attribute", invoke, InterpreterIssue::USCXML_ISSUE_WARNING)); @@ -938,8 +1011,8 @@ NEXT_SET: } { - for (size_t i = 0; i < cancels.size(); i++) { - Element<std::string> cancel = Element<std::string>(cancels[i]); + for (auto iter = cancels.begin(); iter != cancels.end(); iter++) { + DOMElement* cancel = *iter; if (HAS_ATTR(cancel, "sendidexpr")) { if (!_dataModel.isValidSyntax(ATTR(cancel, "sendidexpr"))) { issues.push_back(InterpreterIssue("Syntax error in sendidexpr attribute", cancel, InterpreterIssue::USCXML_ISSUE_WARNING)); @@ -955,5 +1028,29 @@ NEXT_SET: return issues; } +std::ostream& operator<< (std::ostream& os, const InterpreterIssue& issue) { + switch (issue.severity) { + case InterpreterIssue::USCXML_ISSUE_FATAL: + os << "Issue (FATAL) "; + break; + case InterpreterIssue::USCXML_ISSUE_WARNING: + os << "Issue (WARNING) "; + break; + case InterpreterIssue::USCXML_ISSUE_INFO: + os << "Issue (INFO) "; + break; + default: + break; + } + + if (issue.xPath.size() > 0) { + os << "at " << issue.xPath << ": "; + } else { + os << ": "; + } + os << issue.message; + return os; +} + }
\ No newline at end of file diff --git a/src/uscxml/debug/InterpreterIssue.h b/src/uscxml/debug/InterpreterIssue.h index 61b3b6a..f72a7a4 100644 --- a/src/uscxml/debug/InterpreterIssue.h +++ b/src/uscxml/debug/InterpreterIssue.h @@ -23,7 +23,7 @@ #include "uscxml/Common.h" #include <list> #include <iostream> -#include <DOM/Node.hpp> +#include <xercesc/dom/DOMNode.hpp> namespace uscxml { @@ -37,17 +37,17 @@ public: USCXML_ISSUE_INFO }; - InterpreterIssue(const std::string& msg, Arabica::DOM::Node<std::string> node, IssueSeverity severity, const std::string& specRef = ""); + InterpreterIssue(const std::string& msg, xercesc::DOMNode* node, IssueSeverity severity, const std::string& specRef = ""); std::string xPath; std::string message; - Arabica::DOM::Node<std::string> node; + xercesc::DOMNode* node; IssueSeverity severity; std::string specRef; private: static std::list<InterpreterIssue> forInterpreter(InterpreterImpl* interpreter); - friend class InterpreterImpl; + friend class Interpreter; }; USCXML_API std::ostream& operator<< (std::ostream& os, const InterpreterIssue& issue); diff --git a/src/uscxml/debug/SCXMLDotWriter.cpp b/src/uscxml/debug/SCXMLDotWriter.cpp deleted file mode 100644 index 56de37a..0000000 --- a/src/uscxml/debug/SCXMLDotWriter.cpp +++ /dev/null @@ -1,888 +0,0 @@ -/** - * @file - * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) - * @copyright Simplified BSD - * - * @cond - * This program is free software: you can redistribute it and/or modify - * it under the terms of the FreeBSD license as published by the FreeBSD - * project. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the FreeBSD license along with this - * program. If not, see <http://www.opensource.org/licenses/bsd-license>. - * @endcond - */ - -#include "uscxml/Common.h" -#include "uscxml/UUID.h" -#include "SCXMLDotWriter.h" -#include "../transform/FlatStateIdentifier.h" -#include "uscxml/dom/DOMUtils.h" -#include <boost/algorithm/string.hpp> // replace_all -#include <iomanip> - -namespace uscxml { - -using namespace Arabica::DOM; -using namespace Arabica::XPath; - -SCXMLDotWriter::SCXMLDotWriter() { - _iteration = 0; - _indentation = 0; -} - -SCXMLDotWriter::SCXMLDotWriter(Interpreter interpreter, - const std::list<SCXMLDotWriter::StateAnchor>& stateAnchors, - const Element<std::string>& transition) { - _interpreter = interpreter; - _xmlNSPrefix = _interpreter.getNameSpaceInfo().xmlNSPrefix; - _transition = transition; - _anchors = stateAnchors; - - NodeList<std::string > scxmlElems = interpreter.getDocument().getElementsByTagName("scxml"); - _scxml = (Element<std::string>)scxmlElems.item(0); - _isFlat = HAS_ATTR(_scxml, "flat") && stringIsTrue(ATTR(_scxml, "flat")); - - if (_anchors.size() == 0) { - StateAnchor anchor; - anchor.element = _scxml; - _anchors.push_back(anchor); - } - - for (std::list<StateAnchor>::iterator anchIter = _anchors.begin(); anchIter != _anchors.end(); anchIter++) { - if (!anchIter->element) - anchIter->element = _scxml; - if (anchIter->childDepth >= 0 && anchIter->transDepth == -1) - anchIter->transDepth = anchIter->childDepth; - - _portType = anchIter->type; - assembleGraph(anchIter->element, anchIter->childDepth, anchIter->transDepth + 1); - } - - _iteration = 0; - _indentation = 0; -} - -SCXMLDotWriter::~SCXMLDotWriter() { - -} - -void SCXMLDotWriter::onStableConfiguration(Interpreter interpreter) { - std::ostringstream fileSS; - fileSS << interpreter.getName() << "." << std::setw(6) << std::setfill('0') << _iteration++ << ".dot"; - toDot(fileSS.str(), interpreter); -} - -void SCXMLDotWriter::afterCompletion(Interpreter interpreter) { - std::ostringstream fileSS; - fileSS << interpreter.getName() << "." << std::setw(6) << std::setfill('0') << _iteration++ << ".dot"; - toDot(fileSS.str(), interpreter); -} - -void SCXMLDotWriter::beforeMicroStep(Interpreter interpreter) { -// std::ostringstream fileSS; -// fileSS << interpreter.getName() << "." << std::setw(6) << std::setfill('0') << _iteration++ << ".dot"; -// toDot(fileSS.str(), interpreter); -} - -void SCXMLDotWriter::beforeTakingTransition(Interpreter interpreter, - const Element<std::string>& transition, - bool moreComing) { - std::ostringstream fileSS; - fileSS << interpreter.getName() << "." << std::setw(6) << std::setfill('0') << _iteration++ << ".dot"; - toDot(fileSS.str(), interpreter, transition); -} - -std::string SCXMLDotWriter::getPrefix() { - std::string prefix = ""; - for (size_t i = 0; i < _indentation; i++) - prefix += " "; - return prefix; -} - -void SCXMLDotWriter::writeTo(std::ostream& os) { - os << "digraph {" << std::endl; - os << " rankdir=LR;" << std::endl; - os << " fontsize=10;" << std::endl; - // outfile << " splines=ortho;" << std::endl; - // outfile << " splines=false;" << std::endl; - // outfile << " nodesep=1.0;" << std::endl; - - _indentation++; - writeStateElement(os, _scxml); - - // write edges at end of file - for(std::set<DotEdge>::iterator edgeIter = _edges.begin(); edgeIter != _edges.end(); edgeIter++) { - if (edgeIter->from == edgeIter->to) - continue; - if (_histories.find(edgeIter->to) != _histories.end()) { - if (_histories.find(edgeIter->to)->second.from == _histories.find(edgeIter->to)->second.to) - continue; - } - - os << getPrefix() << "\"" << portEscape(edgeIter->from) << "\""; - if (edgeIter->fromPort.size() > 0) { - os << std::string(":\"") + portEscape(edgeIter->fromPort) + "\":e"; - } else { - os << ":__name"; - } - os << " -> "; - - if (_histories.find(edgeIter->to) != _histories.end()) { - os << getPrefix() << "\"" << portEscape(_histories.find(edgeIter->to)->second.to) << "\""; - if (_histories.find(edgeIter->to)->second.toPort.size() > 0) { - os << std::string(":\"") + portEscape(_histories.find(edgeIter->to)->second.toPort) + "\""; - } else { - os << ":__name"; - } - - } else { - os << getPrefix() << "\"" << portEscape(edgeIter->to) << "\""; - if (edgeIter->toPort.size() > 0) { - os << std::string(":\"") + portEscape(edgeIter->toPort) + "\""; - } else { - os << ":__name"; - } - } - - if (edgeIter->type == EDGE_INITAL) { - os << ":nw [style=\"dashed\", color=\"black\"]"; - } else { - os << " [color=\"black\"]"; - } - os << std::endl; - - } - - _indentation--; - - os << "}" << std::endl; - -} - -void SCXMLDotWriter::toDot(const std::string& filename, - Interpreter interpreter, - const std::list<SCXMLDotWriter::StateAnchor>& stateAnchors, - const Element<std::string>& transition) { - - std::ofstream outfile(filename.c_str()); - SCXMLDotWriter writer(interpreter, stateAnchors, transition); - writer.writeTo(outfile); - -} - -/** - * Walk the subset of the graph that is reachable and remember the nodes - */ -void SCXMLDotWriter::assembleGraph(const Element<std::string>& state, int32_t childDepth, int32_t transDepth) { - std::string nodeId = idForNode(state); - - // this node is neither included per child, nor per transition - if (childDepth <= 0 && transDepth <= 0) { - return; - } - - // been here - if (_graph.find(nodeId) != _graph.end()) - return; - - _graph[nodeId].node = state; - _graph[nodeId].portType = _portType; - - - if (childDepth == 0 && transDepth == 0) { - _graph[nodeId].isBorder = true; - } - - - NodeSet<std::string> childElems = DOMUtils::filterChildType(Node_base::ELEMENT_NODE, state); - for (size_t i = 0; i < childElems.size(); i++) { - Element<std::string> childElem(childElems[i]); - - // remember histories we passed - if (iequals(TAGNAME(childElem), "history")) { - _histories[ATTR(childElem, "id")].to = ATTR(state, "id"); - _histories[ATTR(childElem, "id")].toPort = ATTR(childElem, "id"); - } - - // follow transitions - if (iequals(TAGNAME(childElem), "transition")) { - _graph[nodeId].transitions.push_back(childElem); - NodeSet<std::string> targetStates = _interpreter.getImpl()->getTargetStates(childElem); - for (size_t j = 0; j < targetStates.size(); j++) { - std::string remoteNodeId = idForNode(targetStates[j]); - _graph[nodeId].targets.insert(std::make_pair(remoteNodeId, childElem)); - - // recurse along the transition targets, no weight from child depth - assembleGraph((Element<std::string>)targetStates[j], 0, transDepth - 1); - } - if (targetStates.size() == 0) - _graph[nodeId].targets.insert(std::make_pair(nodeId, childElem)); - - std::list<std::string> eventNames; - if (HAS_ATTR(childElem, "event")) - eventNames = tokenize(ATTR(childElem, "event")); - if (eventNames.size() == 0) - _graph[nodeId].events.insert(std::make_pair("", childElem)); - for (std::list<std::string>::iterator evIter = eventNames.begin(); evIter != eventNames.end(); evIter++) { - _graph[nodeId].events.insert(std::make_pair(*evIter, childElem)); - } - } - - // follow childs - if (InterpreterImpl::isState(Element<std::string>(childElem))) { - if (_interpreter.getImpl()->isInitial(Element<std::string>(childElem))) { - // add to initial states if it is initial - _graph[nodeId].initialchilds.insert(idForNode(childElem)); - } else if (_interpreter.getImpl()->isParallel(Element<std::string>(state))) { - // add to initial states if we are parallel - _graph[nodeId].initialchilds.insert(idForNode(childElem)); - } - // in any case, it is a child state - _graph[nodeId].childs.insert(idForNode(childElem)); - - // recurse (do we really need to?) - assembleGraph(childElem, childDepth - 1, transDepth); - } - - } -} - - -/** - * Walk the complete graph and draw reachable subset - */ -void SCXMLDotWriter::writeStateElement(std::ostream& os, const Element<std::string>& stateElem) { - std::string stateId = idForNode(stateElem); - - if (_knownIds.find(stateId) != _knownIds.end()) - return; - _knownIds.insert(stateId); - - std::list<Node<std::string> > invisParents; - bool isVisible = (_graph.find(stateId) != _graph.end()); - - bool subgraph = InterpreterImpl::isCompound(stateElem) || InterpreterImpl::isParallel(stateElem); - bool fatherIsParallel = (stateElem.getParentNode() && - stateElem.getParentNode().getNodeType() == Node_base::ELEMENT_NODE && - InterpreterImpl::isParallel(Element<std::string>(stateElem.getParentNode()))); - - if (subgraph) { - _indentation++; - os << std::endl; - os << getPrefix() << "subgraph \"cluster_" << portEscape(stateId) << "\" {" << std::endl; - _indentation++; - os << getPrefix() << "fontsize=14" << std::endl; - os << getPrefix() << "label=<" << nameForNode(stateElem) << ">" << std::endl; - // os << getPrefix() << "rank=\"same\"" << std::endl; - os << getPrefix() << "labeljust=l" << std::endl; - - os << getPrefix() << (fatherIsParallel ? "style=dashed" : "style=solid") << std::endl; - - // os << getPrefix() << "ranksep=\"equally\"" << std::endl; - - } - - if (isVisible) { - // this state is visible! - const DotState& dotState = _graph.find(stateId)->second; - - // is this a subgraph? - - os << std::endl; - os << getPrefix() << "\"" << portEscape(stateId) << "\" [" << std::endl; - _indentation++; - - os << getPrefix() << "fontsize=10," << std::endl; - os << getPrefix() << "shape=plaintext," << std::endl; - os << getPrefix() << (fatherIsParallel ? "style=dashed," : "style=solid,") << std::endl; - - // is the current state in the basic configuration? - if (InterpreterImpl::isMember(stateElem, _interpreter.getBasicConfiguration())) - os << getPrefix() << "color=red, penwidth=3," << std::endl; - - // is the current state in the basic configuration? - if (dotState.isBorder) - os << getPrefix() << "color=blue," << std::endl; - - // is this state final? - if (_interpreter.getImpl()->isFinal(stateElem)) { - os << getPrefix() << "shape=doublecircle," << std::endl; - os << getPrefix() << "color=black," << std::endl; - os << getPrefix() << "penwidth=2," << std::endl; - os << getPrefix() << "label=< <table cellborder=\"0\" border=\"0\"><tr><td balign=\"left\">" << nameForNode(stateElem) << "</td></tr></table>>" << std::endl; - _indentation--; - os << getPrefix() << "];" << std::endl; - - return; - } - - // is the state initial? - bool isInitial = _interpreter.getImpl()->isInitial(stateElem); - // if (isInitial) - // os << getPrefix() << "style=filled, fillcolor=lightgrey, " << std::endl; - - DotState::mmap_s_e_t::const_iterator destIterF, destIterB; - //std::list<std::string> outPorts; // count unique keys - - int nrOutPorts = 0; - - switch (dotState.portType) { - case PORT_TARGET: // outports are per target - for(DotState::mmap_s_e_t::const_iterator it = dotState.targets.begin(), end = dotState.targets.end(); - it != end; - it = dotState.targets.upper_bound(it->first)) { - nrOutPorts++; - } - break; - case PORT_EVENT: // outports are per event - for(DotState::mmap_s_e_t::const_iterator it = dotState.events.begin(), end = dotState.events.end(); - it != end; - it = dotState.events.upper_bound(it->first)) { - nrOutPorts++; - } - break; - case PORT_TRANSITION: - nrOutPorts = dotState.transitions.size(); -// for (size_t i = 0; i < dotState.transitions.size(); i++) { -// outPorts.push_back(idForNode(dotState.transitions[i])); -// } - break; - } - - os << getPrefix() << "label = < " << std::endl; - - /* - <table cellborder="1" border="0" cellspacing="0" cellpadding="2" style="rounded"> - <tr><td port="name" rowspan="4"><b>step</b></td></tr> - <tr><td port="foo.error.port" align="right">foo.error.port</td></tr> - <tr><td port="bar" align="right">bar</td></tr> - <tr><td port="baz" align="right">baz</td></tr> - </table> - */ - - std::string details = getDetailedLabel(stateElem); - std::string stateLabel = nameForNode(stateElem); - int stateLines = 0; - - std::string::size_type start = 0; - while ((start = stateLabel.find("<br", start)) != std::string::npos) { - ++stateLines; - start += 3; - } - - os << "<table " << (isInitial ? "bgcolor=\"orange\" " : "") << "valign=\"top\" align=\"left\" cellborder=\"1\" border=\"0\" cellspacing=\"0\" cellpadding=\"5\" >" << std::endl; - os << " <tr><td port=\"__name\" balign=\"left\" valign=\"top\" align=\"left\" rowspan=\"" << nrOutPorts + 1 << "\">" << stateLabel << "</td></tr>" << std::endl; - - switch (dotState.portType) { - case PORT_TARGET: // outports are per target - writePerTargetPorts(os, dotState, stateLines); - break; - case PORT_EVENT: // outports are per event - writePerEventPorts(os, dotState, stateLines); - break; - case PORT_TRANSITION: - writePerTransitionPorts(os, dotState, stateLines); - break; - } - - - // write details of the state - if (details.size() > 0) { - os << " <tr><td balign=\"left\" colspan=\"" << (nrOutPorts == 0 ? 1 : 2) << "\">" << std::endl; - os << details << std::endl; - os << " </td></tr>" << std::endl; - } - - // write history states - NodeSet<std::string> histories = DOMUtils::filterChildElements(_xmlNSPrefix + "history", stateElem); - for (size_t i = 0; i < histories.size(); i++) { - os << " <tr><td port=\"" << portEscape(ATTR_CAST(histories[i], "id")) << "\" balign=\"left\" colspan=\"" << (nrOutPorts == 0 ? 1 : 2) << "\"><b>history: </b>" << ATTR_CAST(histories[i], "id") << "</td></tr>" << std::endl; - - } - - os << "</table>" << std::endl << getPrefix() << ">" << std::endl; - - _indentation--; - os << getPrefix() << "];" << std::endl; - - for (std::set<std::string>::iterator initIter = dotState.initialchilds.begin(); initIter != dotState.initialchilds.end(); initIter++) { - std::string destId = *initIter; - DotEdge edge(stateId, destId); - edge.type = EDGE_INITAL; - if (_graph.find(destId) != _graph.end()) - _edges.insert(edge); - } - - } - - // recurse into children and search others to draw - NodeSet<std::string> childElems = DOMUtils::filterChildType(Node_base::ELEMENT_NODE, stateElem); - for (size_t i = 0; i < childElems.size(); i++) { - Element<std::string> childElem(childElems[i]); - if (InterpreterImpl::isState(Element<std::string>(childElem))) { - writeStateElement(os, childElem); - } - } - - if (subgraph) { - _indentation--; - os << getPrefix() << "} #" << stateId << std::endl; - _indentation--; - } -} - -void SCXMLDotWriter::writePerTransitionPorts(std::ostream& os, const DotState& dotState, int stateLines) { - // TODO: Not implemented -} - -void SCXMLDotWriter::writePerEventPorts(std::ostream& os, const DotState& dotState, int stateLines) { - // std::multimap<std::string, Arabica::DOM::Element<std::string> > events; // key is event name, value is transitions that react - - std::string stateId = idForNode(dotState.node); - DotState::mmap_s_e_t::const_iterator destIterF, destIterB; - - for(DotState::mmap_s_e_t::const_iterator it = dotState.events.begin(), end = dotState.events.end(); - it != end; - it = dotState.events.upper_bound(it->first)) { - os << " <tr><td port=\"" << portEscape(it->first) << "\" align=\"right\">" << it->first << "</td></tr>" << std::endl; - } - -} - -std::string SCXMLDotWriter::htmlLabelForId(const std::string& stateId, int minRows) { - FlatStateIdentifier flatId(stateId); - - std::list<std::string>::const_iterator listIter; - std::stringstream labelSS; - std::string seperator; - - labelSS << "<b>active: </b>"; - labelSS << "{"; - for (listIter = flatId.getActive().begin(); listIter != flatId.getActive().end(); listIter++) { - labelSS << seperator << *listIter; - seperator = ", "; - } - labelSS << "}"; - - if (flatId.getVisited().size() > 0) { - minRows--; - - labelSS << "<br /><b>init: </b>"; - - labelSS << "{"; - seperator = ""; - for (listIter = flatId.getVisited().begin(); listIter != flatId.getVisited().end(); listIter++) { - labelSS << seperator << *listIter; - seperator = ", "; - } - labelSS << "}"; - } - - if (flatId.getHistory().size() > 0) { - minRows--; - - seperator = ""; - std::string histSeperator = "<br /> "; - - labelSS << "<br /><b>history: </b>"; - - std::map<std::string, std::list<std::string> >::const_iterator histIter; - for (histIter = flatId.getHistory().begin(); histIter != flatId.getHistory().end(); histIter++) { - labelSS << histSeperator << histIter->first << ": {"; - - for (listIter = histIter->second.begin(); listIter != histIter->second.end(); listIter++) { - labelSS << seperator << *listIter; - seperator = ", "; - } - labelSS << "}"; - seperator = ""; - } - } - return labelSS.str(); -} - - -void SCXMLDotWriter::writePerTargetPorts(std::ostream& os, const DotState& dotState, int stateLines) { - // std::multimap<std::string, Arabica::DOM::Element<std::string> > targets; // key is remote node, transition is element - - int nrOutports = 0; - std::string stateId = idForNode(dotState.node); - - typedef DotState::mmap_s_e_t iter_t; - - // we need to count outports first for vertical padding - for(iter_t::const_iterator targetIter = dotState.targets.begin(), end = dotState.targets.end(); - targetIter != end; - targetIter = dotState.targets.upper_bound(targetIter->first)) { - nrOutports++; - } - - for(iter_t::const_iterator targetIter = dotState.targets.begin(), end = dotState.targets.end(); - targetIter != end; - targetIter = dotState.targets.upper_bound(targetIter->first)) { - - // gather all events that activate the transition - std::string targetId = targetIter->first; - - std::set<std::string> eventNames; - - DotEdge edge(stateId, targetId); - edge.fromPort = targetId; - - std::pair <iter_t::const_iterator, iter_t::const_iterator> targetKeyRange = dotState.targets.equal_range(targetId); - for (iter_t::const_iterator transIter = targetKeyRange.first; transIter != targetKeyRange.second; ++transIter) { - const Element<std::string>& transElem = transIter->second; - - std::list<std::string> events = tokenize(ATTR(transElem, "event")); - eventNames.insert(events.begin(), events.end()); - - if (events.size() == 0) { - // spontaneous transition - eventNames.insert("#"); - edge.type = EDGE_SPONTANEOUS; - } - } - - if (_graph.find(targetId) != _graph.end()) - _edges.insert(edge); - - std::stringstream outPortSS; - outPortSS << (_isFlat ? htmlLabelForId(targetId) : "<b>" + targetId + "</b>" ); - - if (_isFlat) { - outPortSS << "<br /><b>events: </b>{"; - } else { - outPortSS << "<br />{"; - } - - std::string seperator; - for (std::set<std::string>::const_iterator eventIter = eventNames.begin(); eventIter != eventNames.end(); eventIter++) { - outPortSS << seperator << *eventIter << std::endl; - seperator = ", "; - } - outPortSS << "}"; - - if (nrOutports == 1) { - int missing = stateLines - nrOutports; - while (_isFlat && missing-- >= 1) { - outPortSS << "<br /> "; - } - } - - os << " <tr><td port=\"" << portEscape(targetId) << "\" balign=\"left\" align=\"left\">" << outPortSS.str() << "</td></tr>" << std::endl; - - } -} - -std::string SCXMLDotWriter::getDetailedLabel(const Element<std::string>& elem, int indentation) { - - /* - <table> - <tr> - <td colspan="2">onEntry</td> - </tr> - <tr> - <td>Details</td> - <td bgcolor="#eee"> - Nested Content - </td> - </tr> - </table> - */ - - std::list<struct ElemDetails> content; - - NodeList<std::string > childElems = elem.getChildNodes(); - for (size_t i = 0; i < childElems.getLength(); i++) { - if (childElems.item(i).getNodeType() != Node_base::ELEMENT_NODE) - continue; - Element<std::string> elem = Element<std::string>(childElems.item(i)); - - if (InterpreterImpl::isState(elem) || - iequals(TAGNAME(elem), "transition") || - iequals(TAGNAME(elem), "initial") || - false) - continue; - - struct ElemDetails details; - details.name = "<b>" + TAGNAME(elem) + ":</b>"; - - if (iequals(TAGNAME(elem), "history")) { - continue; - } - - // provide details for special elements here - - // param --------- - if (iequals(TAGNAME(elem), "param")) { - if (HAS_ATTR(elem, "name")) - details.name += " " + ATTR(elem, "name") + " = "; - if (HAS_ATTR(elem, "expr")) - details.name += ATTR(elem, "expr"); - if (HAS_ATTR(elem, "location")) - details.name += ATTR(elem, "location"); - } - - // data --------- - if (iequals(TAGNAME(elem), "data")) { - if (HAS_ATTR(elem, "id")) - details.name += " " + ATTR(elem, "id"); - if (HAS_ATTR(elem, "src")) - details.name += ATTR(elem, "src"); - if (HAS_ATTR(elem, "expr")) - details.name += " = " + ATTR(elem, "expr"); - NodeList<std::string > grandChildElems = elem.getChildNodes(); - for (size_t j = 0; j < grandChildElems.getLength(); j++) { - if (grandChildElems.item(j).getNodeType() == Node_base::TEXT_NODE) { - details.name += dotEscape(grandChildElems.item(j).getNodeValue()); - } - } - } - - // invoke --------- - if (iequals(TAGNAME(elem), "invoke")) { - if (HAS_ATTR(elem, "type")) - details.name += "<br />type = " + ATTR(elem, "type"); - if (HAS_ATTR(elem, "typeexpr")) - details.name += "<br />type = " + ATTR(elem, "typeexpr"); - if (HAS_ATTR(elem, "src")) - details.name += "<br />src = " + ATTR(elem, "src"); - if (HAS_ATTR(elem, "srcexpr")) - details.name += "<br />src = " + ATTR(elem, "srcexpr"); - if (HAS_ATTR(elem, "id")) - details.name += "<br />id = " + ATTR(elem, "id"); - if (HAS_ATTR(elem, "idlocation")) - details.name += "<br />id = " + ATTR(elem, "idlocation"); - } - - // send --------- - if (iequals(TAGNAME(elem), "raise")) { - if (HAS_ATTR(elem, "event ")) - details.name += "<br />event = " + ATTR(elem, "event"); - } - - // send --------- - if (iequals(TAGNAME(elem), "send")) { - if (HAS_ATTR(elem, "id")) - details.name += "<br />id = " + ATTR(elem, "id"); - if (HAS_ATTR(elem, "type")) - details.name += "<br />type = " + ATTR(elem, "type"); - if (HAS_ATTR(elem, "typeexpr")) - details.name += "<br />type = " + ATTR(elem, "typeexpr"); - if (HAS_ATTR(elem, "event")) - details.name += "<br />event = " + ATTR(elem, "event"); - if (HAS_ATTR(elem, "eventexpr")) - details.name += "<br />event = " + ATTR(elem, "eventexpr"); - if (HAS_ATTR(elem, "target")) - details.name += "<br />target = " + ATTR(elem, "target"); - if (HAS_ATTR(elem, "targetexpr")) - details.name += "<br />target = " + ATTR(elem, "targetexpr"); - if (HAS_ATTR(elem, "delay")) - details.name += "<br />delay = " + ATTR(elem, "delay"); - if (HAS_ATTR(elem, "delayexpr")) - details.name += "<br />delay = " + ATTR(elem, "delayexpr"); - } - - // cancel --------- - if (iequals(TAGNAME(elem), "cancel")) { - if (HAS_ATTR(elem, "sendid")) - details.name += " " + ATTR(elem, "sendid"); - } - - // script --------- - if (iequals(TAGNAME(elem), "script")) { - details.name += " "; - if (HAS_ATTR(elem, "src")) - details.name += ATTR(elem, "src"); - NodeList<std::string > grandChildElems = childElems.item(i).getChildNodes(); - for (size_t j = 0; j < grandChildElems.getLength(); j++) { - if (grandChildElems.item(j).getNodeType() == Node_base::TEXT_NODE) { - details.name += dotEscape(grandChildElems.item(j).getNodeValue()); - } - } - } - - // if --------- - if (iequals(TAGNAME(elem), "if")) { - if (HAS_ATTR(elem, "cond")) - details.name += " cond = " + dotEscape(ATTR(elem, "cond")); - } - - // elseif --------- - if (iequals(TAGNAME(elem), "elseif")) { - if (HAS_ATTR(elem, "cond")) - details.name += " cond = " + dotEscape(ATTR(elem, "cond")); - } - - // log --------- - if (iequals(TAGNAME(elem), "log")) { - details.name += " "; - if (HAS_ATTR(elem, "label")) - details.name += ATTR(elem, "label") + " = "; - if (HAS_ATTR(elem, "expr")) - details.name += ATTR(elem, "expr"); - } - - // foreach --------- - if (iequals(TAGNAME(elem), "foreach")) { - if (HAS_ATTR(elem, "item")) - details.name += "<br /> item = " + ATTR(elem, "item"); - if (HAS_ATTR(elem, "array")) - details.name += "<br /> array = " + ATTR(elem, "array"); - if (HAS_ATTR(elem, "index")) - details.name += "<br /> index = " + ATTR(elem, "index"); - } - - // recurse - details.content = getDetailedLabel((Element<std::string>)childElems.item(i), indentation + 1); - content.push_back(details); - } - - std::stringstream ssContent; - - if (content.size() > 0) { - ssContent << "<table cellspacing=\"2\" cellpadding=\"0\" border=\"0\" bgcolor=\"#" << colorForIndent(indentation + 1) << "\">"; - - std::list<struct ElemDetails>::iterator contentIter = content.begin(); - while(contentIter != content.end()) { - ssContent << "<tr>"; -// ssContent << "<td align=\"left\" colspan=\"2\">" << contentIter->name << "</td>"; - ssContent << "<td balign=\"left\" align=\"left\">" << contentIter->name << "</td>"; - ssContent << "</tr>"; - - if (contentIter->content.size() > 0) { - ssContent << "<tr>"; -// ssContent << "<td>" << contentIter->details << "</td>"; - ssContent << "<td bgcolor=\"#" << colorForIndent(indentation + 1) << "\">" << contentIter->content << "</td>"; - ssContent << "</tr>"; - } - contentIter++; - - } - ssContent << "</table>"; - } - return ssContent.str(); -} - -std::string SCXMLDotWriter::portEscape(const std::string& text) { - std::string escaped(text); - boost::replace_all(escaped, ".", "-"); - boost::replace_all(escaped, "{", "-"); - boost::replace_all(escaped, "}", "-"); - boost::replace_all(escaped, ":", "-"); - return escaped; -} - -std::string SCXMLDotWriter::dotEscape(const std::string& text) { - std::string escaped(text); - boost::replace_all(escaped, " ", " "); - boost::replace_all(escaped, "\t", " "); - boost::replace_all(escaped, "<", "<"); - boost::replace_all(escaped, ">", ">"); - boost::replace_all(escaped, "\"", """); - boost::replace_all(escaped, "\n", "<br />"); - - return escaped; -} - -std::string SCXMLDotWriter::colorForIndent(int indent) { - int color = 255 - (16 * indent); - std::stringstream ss; - ss << std::hex << color; - ss << std::hex << color; - ss << std::hex << color; - return ss.str(); -} - -std::string SCXMLDotWriter::nameForNode(const Node<std::string>& node) { - std::string elemName; - if (node.getNodeType() == Node_base::ELEMENT_NODE) { - Element<std::string> elem = (Element<std::string>)node; - - if (InterpreterImpl::isFinal(elem) && _isFlat) { - // ignore visited and history with final elements - FlatStateIdentifier flatId(elem.getAttribute("id")); - return "<b>" + flatId.getActive().front() + "</b>"; - } - - if (elem.hasAttribute("id") && _isFlat) { - elemName = htmlLabelForId(elem.getAttribute("id")); - if (elemName.size() > 0) - return elemName; - } else if (elem.getTagName() == "scxml") { - if (elem.hasAttribute("name") && !UUID::isUUID(elem.getAttribute("name"))) { - elemName += elem.getAttribute("name"); - } else if (elem.hasAttribute("id") && !UUID::isUUID(elem.getAttribute("id"))) { - elemName += elem.getAttribute("id"); - } - } else if (InterpreterImpl::isCompound(elem)) { - elemName = "<i>Compound: </i>" + elem.getAttribute("id"); - } else if (InterpreterImpl::isParallel(elem)) { - elemName = "<i>Parallel: </i>" + elem.getAttribute("id"); - } else if (elem.hasAttribute("id")) { - elemName += elem.getAttribute("id"); - } - } - - if (elemName.size() == 0) - elemName = boost::lexical_cast<std::string>(node.getLocalName()); - - return "<b>" + elemName + "</b>"; - -} - -std::string SCXMLDotWriter::idForNode(const Node<std::string>& node) { - std::string elemId; - - // try to get the id as the name or id attribute - if (node.getNodeType() == Node_base::ELEMENT_NODE) { - Element<std::string> elem = (Element<std::string>)node; - -// if (InterpreterImpl::isFinal(elem) && _isFlat) { -// // ignore visited and history with final elements -// FlatStateIdentifier flatId(elem.getAttribute("id")); -// -// std::stringstream activeSS; -// activeSS << "active-"; -// for (std::list<std::string>::const_iterator activeIter = flatId.getActive().begin(); activeIter != flatId.getActive().end(); activeIter++) { -// activeSS << *activeIter << "-"; -// } -// return activeSS.str(); -// } - - if (elem.hasAttribute("name")) { - elemId = elem.getAttribute("name"); - } else if (elem.hasAttribute("id")) { - elemId = elem.getAttribute("id"); - } - } - - // no luck, create id from position in tree - if (elemId.size() == 0) { - Node<std::string> tmpParent = node; - Node<std::string> tmpIndex; - do { - if (tmpParent.getNodeType() != Node_base::ELEMENT_NODE) - continue; - - tmpIndex = tmpParent; - int index = 0; - - while((tmpIndex = tmpIndex.getPreviousSibling())) - index++; - - std::stringstream ssElemId; - ssElemId << TAGNAME_CAST(tmpParent) << index << "."; - elemId = ssElemId.str() + elemId; - } while ((tmpParent = tmpParent.getParentNode())); -// elemId = ssElemId.str(); - } - return elemId; -} - -}
\ No newline at end of file diff --git a/src/uscxml/debug/SCXMLDotWriter.h b/src/uscxml/debug/SCXMLDotWriter.h deleted file mode 100644 index f6c8b3d..0000000 --- a/src/uscxml/debug/SCXMLDotWriter.h +++ /dev/null @@ -1,201 +0,0 @@ -/** - * @file - * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) - * @copyright Simplified BSD - * - * @cond - * This program is free software: you can redistribute it and/or modify - * it under the terms of the FreeBSD license as published by the FreeBSD - * project. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the FreeBSD license along with this - * program. If not, see <http://www.opensource.org/licenses/bsd-license>. - * @endcond - */ - -#ifndef SCXMLDOTWRITER_H_AOP0OHXX -#define SCXMLDOTWRITER_H_AOP0OHXX - -#include "uscxml/Interpreter.h" -#include <DOM/Document.hpp> -#include <XPath/XPath.hpp> -#include <fstream> -#include <set> - -#undef max -#include <limits> - -namespace uscxml { - -class Interpreter; - - - -/** - * This writer, added as a monitor will output .dot files. - * - * # create a set of pdfs form the dot files - * $ dot -Tpdf -O *.dot - * or - * $ find . -name "*.dot" -exec dot -Tpdf -O {} \; - * - * # create a movie from the pdfs - * $ dot -Tgif -O *.dot - * or - * $ find . -name "*.dot" -exec dot -Tgif -O {} \; - * - * $ ffmpeg -r 3 -i <NAME>.%06d.dot.gif -r 25 movie.mpg - * $ convert -delay 20 *.gif animated.gif - * - * # unflatten can be used to create more compact graphs - * find . -name "*.dot" -exec unflatten -f -l2 -o {}.flat.dot {} \; - */ -class USCXML_API SCXMLDotWriter : public InterpreterMonitor { -public: - - enum PortType { - PORT_TARGET, - PORT_EVENT, - PORT_TRANSITION - }; - - struct StateAnchor { - StateAnchor() : childDepth(std::numeric_limits<int32_t>::max()), transDepth(std::numeric_limits<int32_t>::max()), type(PORT_TARGET) {} - Arabica::DOM::Element<std::string> element; - int32_t childDepth; - int32_t transDepth; - - PortType type; - - operator bool() const { - return childDepth != -1 || transDepth != -1 || element; - } - }; - - struct ElemDetails { - std::string name; - std::string details; - std::string content; - }; - - struct DotState { - DotState() : isBorder(false), portType(PORT_TARGET) {} - Arabica::DOM::Element<std::string> node; - Arabica::XPath::NodeSet<std::string> transitions; - std::multimap<std::string, Arabica::DOM::Element<std::string> > targets; // key is remote node, transition is element - std::multimap<std::string, Arabica::DOM::Element<std::string> > events; // key is event name, value is transitions that react - - bool isBorder; - PortType portType; - - std::set<std::string> childs; - std::set<std::string> initialchilds; - - typedef std::multimap<std::string, Arabica::DOM::Element<std::string> > mmap_s_e_t; - }; - - enum EdgeType { - EDGE_INITAL, - EDGE_TRANSIION, - EDGE_SPONTANEOUS - }; - - struct DotEdge { - DotEdge() : type(EDGE_TRANSIION) {} - DotEdge(const std::string& from, const std::string& to) : type(EDGE_TRANSIION), from(from), to(to) { - } - - bool operator< (const DotEdge& other) const { - return from + fromPort + to + toPort > other.from + other.fromPort + other.to + other.toPort; - } - - EdgeType type; - std::string from; - std::string fromCompass; - std::string fromPort; - std::string to; - std::string toPort; - std::string toCompass; - }; - - SCXMLDotWriter(); - ~SCXMLDotWriter(); - - virtual void onStableConfiguration(Interpreter interpreter); - virtual void afterCompletion(Interpreter interpreter); - virtual void beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, bool moreComing); - virtual void beforeMicroStep(Interpreter interpreter); - - static std::string htmlLabelForId(const std::string& stateId, int minRows = 0); - - static void toDot(const std::string& filename, - Interpreter interpreter, - const Arabica::DOM::Element<std::string>& transition = Arabica::DOM::Element<std::string>()) { - std::list<SCXMLDotWriter::StateAnchor> emptyAnchors = std::list<SCXMLDotWriter::StateAnchor>(); - toDot(filename, interpreter, emptyAnchors, transition); - } - - - static void toDot(const std::string& filename, - Interpreter interpreter, - const std::list<SCXMLDotWriter::StateAnchor>& stateAnchors, - const Arabica::DOM::Element<std::string>& transition = Arabica::DOM::Element<std::string>()); - - void writeTo(std::ostream& os); - - std::string getDetailedLabel(const Arabica::DOM::Element<std::string>& elem, int indentation = 0); - std::string colorForIndent(int indent); - - std::string idForNode(const Arabica::DOM::Node<std::string>& node); - std::string nameForNode(const Arabica::DOM::Node<std::string>& node); - std::string getPrefix(); - - static std::string dotEscape(const std::string& text); - static std::string portEscape(const std::string& text); - -protected: - - SCXMLDotWriter(Interpreter interpreter, - const std::list<SCXMLDotWriter::StateAnchor>& stateAnchors, - const Arabica::DOM::Element<std::string>& transition); - - void assembleGraph(const Arabica::DOM::Element<std::string>& start, - int32_t childDepth = std::numeric_limits<int32_t>::max(), - int32_t transDepth = std::numeric_limits<int32_t>::max()); - void writeStateElement(std::ostream& os, const Arabica::DOM::Element<std::string>& state); - - void writePerTransitionPorts(std::ostream& os, const DotState& dotState, int stateLines = 0); - void writePerEventPorts(std::ostream& os, const DotState& dotState, int stateLines = 0); - void writePerTargetPorts(std::ostream& os, const DotState& dotState, int stateLines = 0); - - void writeUnknownNode(std::ostream& os, const std::string& targetId); - - int _iteration; - std::set<std::string> _knownIds; - std::set<std::string> _unknownNodes; - int _indentation; - - std::map<std::string, DotState> _graph; - std::set<DotEdge> _edges; - - // these are only set in ephemeral instances per monitor call - Arabica::DOM::Element<std::string> _transition; - Arabica::DOM::Element<std::string> _scxml; - Interpreter _interpreter; - bool _isFlat; - - std::string _xmlNSPrefix; - std::list<StateAnchor> _anchors; - std::map<std::string, DotEdge> _histories; - - PortType _portType; - -}; - -} - -#endif /* end of include guard: SCXMLDOTWRITER_H_AOP0OHXX */ |