summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2014-08-22 16:21:18 (GMT)
committerStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2014-08-22 16:21:18 (GMT)
commit9509b795c7493d7b351dc25855a6a82cd392deb1 (patch)
tree11f1950bab743ecbe2ac8d75177702a3620ef15c /src
parentd6fa07c2b4b3eb0c8db4f79927f88de72892dcc2 (diff)
downloaduscxml-9509b795c7493d7b351dc25855a6a82cd392deb1.zip
uscxml-9509b795c7493d7b351dc25855a6a82cd392deb1.tar.gz
uscxml-9509b795c7493d7b351dc25855a6a82cd392deb1.tar.bz2
Moved InterpreterIssues to own source file
Diffstat (limited to 'src')
-rw-r--r--src/uscxml/CMakeLists.txt2
-rw-r--r--src/uscxml/Interpreter.cpp519
-rw-r--r--src/uscxml/Interpreter.h21
-rw-r--r--src/uscxml/debug/InterpreterIssue.cpp656
-rw-r--r--src/uscxml/debug/InterpreterIssue.h54
5 files changed, 725 insertions, 527 deletions
diff --git a/src/uscxml/CMakeLists.txt b/src/uscxml/CMakeLists.txt
index d74f512..51ee4ea 100644
--- a/src/uscxml/CMakeLists.txt
+++ b/src/uscxml/CMakeLists.txt
@@ -84,7 +84,7 @@ list (APPEND USCXML_FILES ${USCXML_PLUGINS})
if (BUILD_AS_PLUGINS)
list (APPEND USCXML_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/plugins)
- file(GLOB PLUMA
+ file(GLOB PLUMA
plugins/Pluma/*.cpp
plugins/Pluma/*.h
plugins/*.cpp
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index 370bef0..b9e7145 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -713,513 +713,8 @@ void InterpreterImpl::reset() {
setInterpreterState(USCXML_INSTANTIATED);
}
-InterpreterIssue::InterpreterIssue(const std::string& msg, Arabica::DOM::Node<std::string> node, IssueSeverity severity) : message(msg), node(node), severity(severity) {
- if (node)
- xPath = DOMUtils::xPathForNode(node);
-}
-
std::list<InterpreterIssue> InterpreterImpl::validate() {
- // some things we need to prepare first
- if (_factory == NULL)
- _factory = Factory::getInstance();
- setupDOM();
-
- std::list<InterpreterIssue> issues;
-
- if (!_scxml) {
- InterpreterIssue issue("No SCXML element to be found", Node<std::string>(), InterpreterIssue::USCXML_ISSUE_FATAL);
- issues.push_back(issue);
- return issues;
- }
-
- _cachedStates.clear();
-
- NodeSet<std::string> scxmls = filterChildElements(_nsInfo.xmlNSPrefix + "scxml", _scxml, true);
- scxmls.push_back(_scxml);
-
- NodeSet<std::string> states = filterChildElements(_nsInfo.xmlNSPrefix + "state", _scxml, true);
- NodeSet<std::string> parallels = filterChildElements(_nsInfo.xmlNSPrefix + "parallel", _scxml, true);
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", _scxml, true);
- NodeSet<std::string> initials = filterChildElements(_nsInfo.xmlNSPrefix + "initial", _scxml, true);
- NodeSet<std::string> finals = filterChildElements(_nsInfo.xmlNSPrefix + "final", _scxml, true);
- NodeSet<std::string> onEntries = filterChildElements(_nsInfo.xmlNSPrefix + "onentry", _scxml, true);
- NodeSet<std::string> onExits = filterChildElements(_nsInfo.xmlNSPrefix + "onexit", _scxml, true);
- NodeSet<std::string> histories = filterChildElements(_nsInfo.xmlNSPrefix + "history", _scxml, true);
-
- NodeSet<std::string> raises = filterChildElements(_nsInfo.xmlNSPrefix + "raise", _scxml, true);
- NodeSet<std::string> ifs = filterChildElements(_nsInfo.xmlNSPrefix + "if", _scxml, true);
- NodeSet<std::string> elseIfs = filterChildElements(_nsInfo.xmlNSPrefix + "elseif", _scxml, true);
- NodeSet<std::string> elses = filterChildElements(_nsInfo.xmlNSPrefix + "else", _scxml, true);
- NodeSet<std::string> foreachs = filterChildElements(_nsInfo.xmlNSPrefix + "foreach", _scxml, true);
- NodeSet<std::string> logs = filterChildElements(_nsInfo.xmlNSPrefix + "log", _scxml, true);
-
- NodeSet<std::string> dataModels = filterChildElements(_nsInfo.xmlNSPrefix + "datamodel", _scxml, true);
- NodeSet<std::string> datas = filterChildElements(_nsInfo.xmlNSPrefix + "data", _scxml, true);
- NodeSet<std::string> assigns = filterChildElements(_nsInfo.xmlNSPrefix + "assign", _scxml, true);
- NodeSet<std::string> doneDatas = filterChildElements(_nsInfo.xmlNSPrefix + "donedata", _scxml, true);
- NodeSet<std::string> contents = filterChildElements(_nsInfo.xmlNSPrefix + "content", _scxml, true);
- NodeSet<std::string> params = filterChildElements(_nsInfo.xmlNSPrefix + "param", _scxml, true);
- NodeSet<std::string> scripts = filterChildElements(_nsInfo.xmlNSPrefix + "script", _scxml, true);
-
- NodeSet<std::string> sends = filterChildElements(_nsInfo.xmlNSPrefix + "send", _scxml, true);
- NodeSet<std::string> cancels = filterChildElements(_nsInfo.xmlNSPrefix + "cancel", _scxml, true);
- NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _scxml, true);
- NodeSet<std::string> finalizes = filterChildElements(_nsInfo.xmlNSPrefix + "finalize", _scxml, true);
-
- 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(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::set<std::string> execContentSet;
- execContentSet.insert("raise");
- execContentSet.insert("if");
- execContentSet.insert("elseif");
- execContentSet.insert("else");
- execContentSet.insert("foreach");
- execContentSet.insert("log");
- execContentSet.insert("send");
- execContentSet.insert("assign");
- execContentSet.insert("script");
- execContentSet.insert("cancel");
-
- std::map<std::string, std::set<std::string> > validChildren;
- validChildren["scxml"].insert("state");
- validChildren["scxml"].insert("parallel");
- validChildren["scxml"].insert("final");
- validChildren["scxml"].insert("datamodel");
- validChildren["scxml"].insert("script");
-
- validChildren["state"].insert("onentry");
- validChildren["state"].insert("onexit");
- validChildren["state"].insert("transition");
- validChildren["state"].insert("initial");
- validChildren["state"].insert("state");
- validChildren["state"].insert("parallel");
- validChildren["state"].insert("final");
- validChildren["state"].insert("history");
- validChildren["state"].insert("datamodel");
- validChildren["state"].insert("invoke");
-
- validChildren["parallel"].insert("onentry");
- validChildren["parallel"].insert("onexit");
- validChildren["parallel"].insert("transition");
- validChildren["parallel"].insert("state");
- validChildren["parallel"].insert("parallel");
- validChildren["parallel"].insert("history");
- validChildren["parallel"].insert("datamodel");
- validChildren["parallel"].insert("invoke");
-
- validChildren["transition"] = execContentSet;
- validChildren["onentry"] = execContentSet;
- validChildren["onexit"] = execContentSet;
- validChildren["finalize"] = execContentSet;
-
- validChildren["if"] = execContentSet;
- validChildren["elseif"] = execContentSet;
- validChildren["else"] = execContentSet;
- validChildren["foreach"] = execContentSet;
-
- validChildren["initial"].insert("transition");
- validChildren["history"].insert("transition");
-
- validChildren["final"].insert("onentry");
- validChildren["final"].insert("onexit");
- validChildren["final"].insert("donedata");
-
- validChildren["datamodel"].insert("data");
-
- validChildren["donedata"].insert("content");
- validChildren["donedata"].insert("param");
-
- validChildren["send"].insert("content");
- validChildren["send"].insert("param");
-
- validChildren["invoke"].insert("content");
- validChildren["invoke"].insert("param");
- validChildren["invoke"].insert("finalize");
-
- std::map<std::string, std::set<std::string> > validParents;
- for (std::map<std::string, std::set<std::string> >::iterator parentIter = validChildren.begin(); parentIter != validChildren.end(); parentIter++) {
- for (std::set<std::string>::iterator childIter = parentIter->second.begin(); childIter != parentIter->second.end(); childIter++) {
- validParents[*childIter].insert(parentIter->first);
- }
- }
-
- for (int i = 0; i < allStates.size(); i++) {
- Element<std::string> state = Element<std::string>(allStates[i]);
-
- if (isMember(state, finals) && !HAS_ATTR(state, "id")) // id is not required for finals
- continue;
-
- // check for existance of id attribute
- if (!HAS_ATTR(state, "id")) {
- issues.push_back(InterpreterIssue("State has no 'id' attribute", state, InterpreterIssue::USCXML_ISSUE_FATAL));
- continue;
- }
- std::string stateId = ATTR(state, "id");
-
- // check for uniqueness of id attribute
- if (_cachedStates.find(stateId) != _cachedStates.end()) {
- issues.push_back(InterpreterIssue("Duplicate state with id '" + stateId + "'", state, InterpreterIssue::USCXML_ISSUE_FATAL));
- continue;
- }
- _cachedStates[ATTR(state, "id")] = state;
- }
-
- for (int i = 0; i < transitions.size(); i++) {
- Element<std::string> transition = Element<std::string>(transitions[i]);
-
- // check for valid target
- std::list<std::string> targetIds = InterpreterImpl::tokenizeIdRefs(ATTR(transition, "target"));
- for (std::list<std::string>::iterator targetIter = targetIds.begin(); targetIter != targetIds.end(); targetIter++) {
- if (_cachedStates.find(*targetIter) == _cachedStates.end()) {
- issues.push_back(InterpreterIssue("Transition has non-existant target state with id '" + *targetIter + "'", transition, InterpreterIssue::USCXML_ISSUE_FATAL));
- continue;
- }
- }
- }
-
- // check for redundancy of transition
- for (int i = 0; i < allStates.size(); i++) {
- Element<std::string> state = Element<std::string>(allStates[i]);
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", state, false);
-
- transitions.to_document_order();
-
- for (int j = 1; j < transitions.size(); j++) {
- Element<std::string> transition = Element<std::string>(transitions[j]);
- for (int k = 0; k < j; k++) {
- Element<std::string> earlierTransition = Element<std::string>(transitions[k]);
-
- // will the earlier transition always be enabled when the later is?
- if (!HAS_ATTR(earlierTransition, "cond")) {
- // earlier transition has no condition -> check event descriptor
- if (!HAS_ATTR(earlierTransition, "event")) {
- // earlier transition is eventless
- issues.push_back(InterpreterIssue("Transition can never be optimally enabled", transition, InterpreterIssue::USCXML_ISSUE_INFO));
- goto NEXT_TRANSITION;
-
- } else if (HAS_ATTR(transition, "event")) {
- // does the earlier transition match all our events?
- std::list<std::string> events = InterpreterImpl::tokenizeIdRefs(ATTR(transition, "event"));
-
- bool allMatched = true;
- for (std::list<std::string>::iterator eventIter = events.begin(); eventIter != events.end(); eventIter++) {
- if (!nameMatch(ATTR(earlierTransition, "event"), *eventIter)) {
- allMatched = false;
- break;
- }
- }
-
- if (allMatched) {
- issues.push_back(InterpreterIssue("Transition can never be optimally enabled", transition, InterpreterIssue::USCXML_ISSUE_INFO));
- goto NEXT_TRANSITION;
- }
- }
- }
- }
- NEXT_TRANSITION:;
- }
- }
-
- // check for valid initial attribute
- {
- NodeSet<std::string> withInitialAttr;
- withInitialAttr.push_back(allStates);
- withInitialAttr.push_back(_scxml);
-
- for (int i = 0; i < withInitialAttr.size(); i++) {
- Element<std::string> state = Element<std::string>(withInitialAttr[i]);
- if (HAS_ATTR(state, "initial")) {
- std::list<std::string> intials = InterpreterImpl::tokenizeIdRefs(ATTR(state, "initial"));
- for (std::list<std::string>::iterator initIter = intials.begin(); initIter != intials.end(); initIter++) {
- if (_cachedStates.find(*initIter) == _cachedStates.end()) {
- issues.push_back(InterpreterIssue("Initial attribute has invalid target state with id '" + *initIter + "'", state, InterpreterIssue::USCXML_ISSUE_FATAL));
- continue;
- }
- }
- }
- }
- }
-
- // check that all invokers exists
- {
- for (int i = 0; i < invokes.size(); i++) {
- Element<std::string> invoke = Element<std::string>(invokes[i]);
- if (HAS_ATTR(invoke, "type") && !_factory->hasInvoker(ATTR(invoke, "type"))) {
- issues.push_back(InterpreterIssue("Invoke with unknown type '" + ATTR(invoke, "type") + "'", invoke, InterpreterIssue::USCXML_ISSUE_FATAL));
- continue;
- }
- }
- }
-
- // check that all io processors exists
- {
- for (int i = 0; i < sends.size(); i++) {
- Element<std::string> send = Element<std::string>(sends[i]);
- if (HAS_ATTR(send, "type") && !_factory->hasIOProcessor(ATTR(send, "type"))) {
- issues.push_back(InterpreterIssue("Send to unknown IO Processor '" + ATTR(send, "type") + "'", send, InterpreterIssue::USCXML_ISSUE_FATAL));
- continue;
- }
- }
- }
-
- // 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 (int i = 0; i < allExecContentContainers.size(); i++) {
- Element<std::string> block = Element<std::string>(allExecContentContainers[i]);
- NodeSet<std::string> execContents = filterChildType(Node_base::ELEMENT_NODE, block);
- for (int j = 0; j < execContents.size(); j++) {
- Element<std::string> execContent = Element<std::string>(execContents[j]);
- // SCXML specific executable content, always available
- if (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));
- continue;
- }
- }
- }
- }
-
- // check that all SCXML elements have valid parents
- {
- for (int i = 0; i < allElements.size(); i++) {
- Element<std::string> element = Element<std::string>(allElements[i]);
- std::string localName = LOCALNAME(element);
- if (!element.getParentNode() || element.getParentNode().getNodeType() != Node_base::ELEMENT_NODE) {
- issues.push_back(InterpreterIssue("Parent of " + localName + " is no element", element, InterpreterIssue::USCXML_ISSUE_INFO));
- continue;
- }
-
- Element<std::string> parent = Element<std::string>(element.getParentNode());
- std::string parentName = LOCALNAME(parent);
-
- if (validParents[localName].find(parentName) == validParents[localName].end()) {
- issues.push_back(InterpreterIssue("Element " + parentName + " can be no parent of " + localName, element, InterpreterIssue::USCXML_ISSUE_INFO));
- continue;
- }
- }
- }
-
-
- // check that the datamodel is known
- if (HAS_ATTR(_scxml, "datamodel")) {
- if (!_factory->hasDataModel(ATTR(_scxml, "datamodel"))) {
- issues.push_back(InterpreterIssue("SCXML document requires unknown datamodel '" + ATTR(_scxml, "datamodel") + "'", _scxml, InterpreterIssue::USCXML_ISSUE_FATAL));
-
- // we cannot even check the rest as we require a datamodel
- return issues;
- }
- }
-
- bool instantiatedDataModel = false;
- // instantiate datamodel if not explicitly set
- if (!_dataModel) {
- if (HAS_ATTR(_scxml, "datamodel")) {
- // might throw
- _dataModel = _factory->createDataModel(ATTR(_scxml, "datamodel"), this);
- instantiatedDataModel = true;
- } else {
- _dataModel = _factory->createDataModel("null", this);
- }
- }
-
-
- // test all scripts for valid syntax
- {
- for (int i = 0; i < scripts.size(); i++) {
- Element<std::string> script = Element<std::string>(scripts[i]);
-
- 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();
- }
-
- if (!_dataModel.isValidSyntax(scriptContent)) {
- issues.push_back(InterpreterIssue("Syntax error in script", script, InterpreterIssue::USCXML_ISSUE_WARNING));
- }
- }
- }
- }
-
- // 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);
-
- for (int i = 0; i < withCondAttrs.size(); i++) {
- Element<std::string> condAttr = Element<std::string>(withCondAttrs[i]);
- 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));
- continue;
- }
- }
- }
- }
-
- {
- 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 (int i = 0; i < withExprAttrs.size(); i++) {
- Element<std::string> withExprAttr = Element<std::string>(withExprAttrs[i]);
- if (HAS_ATTR(withExprAttr, "expr")) {
- if (isMember(withExprAttr, datas) || 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;
- }
- } else {
- if (!_dataModel.isValidSyntax(ATTR(withExprAttr, "expr"))) {
- issues.push_back(InterpreterIssue("Syntax error in expr attribute", withExprAttr, InterpreterIssue::USCXML_ISSUE_WARNING));
- continue;
- }
- }
- }
- }
- }
-
- {
- for (int i = 0; i < foreachs.size(); i++) {
- Element<std::string> foreach = Element<std::string>(foreachs[i]);
- 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));
- }
- }
- if (HAS_ATTR(foreach, "item")) {
- if (!_dataModel.isValidSyntax(ATTR(foreach, "item"))) {
- issues.push_back(InterpreterIssue("Syntax error in item attribute", foreach, InterpreterIssue::USCXML_ISSUE_WARNING));
- }
- }
- if (HAS_ATTR(foreach, "index")) {
- if (!_dataModel.isValidSyntax(ATTR(foreach, "index"))) {
- issues.push_back(InterpreterIssue("Syntax error in index attribute", foreach, InterpreterIssue::USCXML_ISSUE_WARNING));
- }
- }
- }
- }
-
- {
- for (int i = 0; i < sends.size(); i++) {
- Element<std::string> send = Element<std::string>(sends[i]);
- 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));
- }
- }
- if (HAS_ATTR(send, "targetexpr")) {
- if (!_dataModel.isValidSyntax(ATTR(send, "targetexpr"))) {
- issues.push_back(InterpreterIssue("Syntax error in targetexpr attribute", send, InterpreterIssue::USCXML_ISSUE_WARNING));
- }
- }
- if (HAS_ATTR(send, "typeexpr")) {
- if (!_dataModel.isValidSyntax(ATTR(send, "typeexpr"))) {
- issues.push_back(InterpreterIssue("Syntax error in typeexpr attribute", send, InterpreterIssue::USCXML_ISSUE_WARNING));
- }
- }
- if (HAS_ATTR(send, "idlocation")) {
- if (!_dataModel.isValidSyntax(ATTR(send, "idlocation"))) {
- issues.push_back(InterpreterIssue("Syntax error in idlocation attribute", send, InterpreterIssue::USCXML_ISSUE_WARNING));
- }
- }
- if (HAS_ATTR(send, "delayexpr")) {
- if (!_dataModel.isValidSyntax(ATTR(send, "delayexpr"))) {
- issues.push_back(InterpreterIssue("Syntax error in delayexpr attribute", send, InterpreterIssue::USCXML_ISSUE_WARNING));
- }
- }
- }
-
- }
-
- {
- for (int i = 0; i < invokes.size(); i++) {
- Element<std::string> invoke = Element<std::string>(invokes[i]);
- 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));
- continue;
- }
- }
- if (HAS_ATTR(invoke, "srcexpr")) {
- if (!_dataModel.isValidSyntax(ATTR(invoke, "srcexpr"))) {
- issues.push_back(InterpreterIssue("Syntax error in srcexpr attribute", invoke, InterpreterIssue::USCXML_ISSUE_WARNING));
- continue;
- }
- }
- if (HAS_ATTR(invoke, "idlocation")) {
- if (!_dataModel.isValidSyntax(ATTR(invoke, "idlocation"))) {
- issues.push_back(InterpreterIssue("Syntax error in idlocation attribute", invoke, InterpreterIssue::USCXML_ISSUE_WARNING));
- continue;
- }
- }
- }
- }
-
- {
- for (int i = 0; i < cancels.size(); i++) {
- Element<std::string> cancel = Element<std::string>(cancels[i]);
- 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));
- continue;
- }
- }
- }
- }
-
- if (instantiatedDataModel)
- _dataModel = DataModel();
-
- return issues;
+ return InterpreterIssue::forInterpreter(this);
}
std::ostream& operator<< (std::ostream& os, const InterpreterIssue& issue) {
@@ -2686,8 +2181,16 @@ Arabica::XPath::NodeSet<std::string> InterpreterImpl::getInitialStates(Arabica::
std::cout << "Getting initial state of " << TAGNAME(state) << " " << ATTR(state, "id") << std::endl;
#endif
+ if (isAtomic(state)) {
+ return Arabica::XPath::NodeSet<std::string>();
+ }
+
assert(isCompound(state) || isParallel(state));
+ if (isParallel(state)) {
+ return getChildStates(state);
+ }
+
// initial attribute at element
Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)state;
if (stateElem.hasAttribute("initial")) {
@@ -3003,8 +2506,8 @@ bool InterpreterImpl::isInitial(const Arabica::DOM::Element<std::string>& state)
if (isMember(state, getInitialStates(parent)))
return true; // every nested node
- if (isParallel(parent))
- return true;
+// if (isParallel(parent))
+// return true;
return false;
}
diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h
index 984e861..b2ea027 100644
--- a/src/uscxml/Interpreter.h
+++ b/src/uscxml/Interpreter.h
@@ -39,6 +39,7 @@
#include "uscxml/concurrency/BlockingQueue.h"
#include "uscxml/messages/Data.h"
#include "uscxml/messages/SendRequest.h"
+#include "uscxml/debug/InterpreterIssue.h"
#include "uscxml/URL.h"
#include "uscxml/plugins/DataModel.h"
@@ -215,24 +216,6 @@ enum InterpreterState {
};
USCXML_API std::ostream& operator<< (std::ostream& os, const InterpreterState& interpreterState);
-class USCXML_API InterpreterIssue {
-public:
- enum IssueSeverity {
- USCXML_ISSUE_FATAL,
- USCXML_ISSUE_WARNING,
- USCXML_ISSUE_INFO
- };
-
- InterpreterIssue(const std::string& msg, Arabica::DOM::Node<std::string> node, IssueSeverity severity);
-
- std::string xPath;
- std::string message;
- Arabica::DOM::Node<std::string> node;
- IssueSeverity severity;
-};
-USCXML_API std::ostream& operator<< (std::ostream& os, const InterpreterIssue& issue);
-
-
class USCXML_API InterpreterImpl : public boost::enable_shared_from_this<InterpreterImpl> {
public:
@@ -571,6 +554,8 @@ protected:
friend class USCXMLInvoker;
friend class SCXMLIOProcessor;
friend class Interpreter;
+ friend class InterpreterIssue;
+
};
class USCXML_API Interpreter {
diff --git a/src/uscxml/debug/InterpreterIssue.cpp b/src/uscxml/debug/InterpreterIssue.cpp
new file mode 100644
index 0000000..60f8f1b
--- /dev/null
+++ b/src/uscxml/debug/InterpreterIssue.cpp
@@ -0,0 +1,656 @@
+/**
+ * @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 <string>
+
+#include "InterpreterIssue.h"
+#include "uscxml/DOMUtils.h"
+#include "uscxml/Interpreter.h"
+#include "uscxml/Factory.h"
+
+#include <XPath/XPath.hpp>
+#include <DOM/Document.hpp>
+
+namespace uscxml {
+
+using namespace Arabica::XPath;
+using namespace Arabica::DOM;
+
+InterpreterIssue::InterpreterIssue(const std::string& msg, Arabica::DOM::Node<std::string> node, IssueSeverity severity) : message(msg), node(node), severity(severity) {
+ 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)
+ continue;
+ // std::cout << TAGNAME(childs.item(i)) << std::endl;
+
+ 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));
+ }
+ assembleNodeSets(nsPrefix, childs.item(i), sets);
+ }
+}
+
+NodeSet<std::string> getReachableStates(const Node<std::string>& scxml, InterpreterImpl* interpreter, const std::string& nsPrefix) {
+ /** Check whether this state is reachable */
+
+ NodeSet<std::string> reachable;
+ reachable.push_back(scxml);
+
+ bool hasChanges = true;
+
+ while (hasChanges) {
+ // iterate initials and transitions until stable
+
+ hasChanges = false;
+ // reachable per initial attribute or document order - size will increase as we append new states
+ for (int i = 0; i < reachable.size(); i++) {
+ // get the state's initial states
+ Element<std::string> state = Element<std::string>(reachable[i]);
+ try {
+ NodeSet<std::string> initials = interpreter->getInitialStates(state);
+ for (int j = 0; j < initials.size(); j++) {
+ Element<std::string> initial = Element<std::string>(initials[j]);
+ if (!InterpreterImpl::isMember(initial, reachable)) {
+ reachable.push_back(initial);
+ hasChanges = true;
+ }
+ }
+ } catch (Event e) {
+ }
+ }
+
+ // reachable per target attribute in transitions
+ for (int i = 0; i < reachable.size(); i++) {
+ Element<std::string> state = Element<std::string>(reachable[i]);
+ NodeSet<std::string> transitions = InterpreterImpl::filterChildElements(nsPrefix + "transition", state, false);
+ for (int j = 0; j < transitions.size(); j++) {
+ Element<std::string> transition = Element<std::string>(transitions[j]);
+ try {
+ NodeSet<std::string> targets = interpreter->getTargetStates(transition);
+ for (int k = 0; k < targets.size(); k++) {
+ Element<std::string> target = Element<std::string>(targets[k]);
+ if (!InterpreterImpl::isMember(target, reachable)) {
+ reachable.push_back(target);
+ hasChanges = true;
+ }
+ }
+ } catch (Event e) {
+ }
+ }
+ }
+ }
+
+ // reachable via a reachable child state
+ for (int i = 0; i < reachable.size(); i++) {
+ Element<std::string> state = Element<std::string>(reachable[i]);
+ if (InterpreterImpl::isAtomic(state)) {
+ // iterate the states parents
+ Node<std::string> parent = state.getParentNode();
+ while(parent && parent.getNodeType() == Node_base::ELEMENT_NODE) {
+ Element<std::string> parentElem = Element<std::string>(parent);
+ if (!InterpreterImpl::isState(parentElem)) {
+ break;
+ }
+ if (!InterpreterImpl::isMember(parentElem, reachable)) {
+ reachable.push_back(parent);
+ }
+ parent = parent.getParentNode();
+ }
+ }
+ }
+
+ return reachable;
+}
+
+
+std::list<InterpreterIssue> InterpreterIssue::forInterpreter(InterpreterImpl* interpreter) {
+ // some things we need to prepare first
+ if (interpreter->_factory == NULL)
+ interpreter->_factory = Factory::getInstance();
+ interpreter->setupDOM();
+
+ std::list<InterpreterIssue> issues;
+
+ if (!interpreter->_scxml) {
+ InterpreterIssue issue("No SCXML element to be found", Node<std::string>(), InterpreterIssue::USCXML_ISSUE_FATAL);
+ issues.push_back(issue);
+ return issues;
+ }
+
+ std::map<std::string, Arabica::DOM::Element<std::string> > seenStates;
+
+ // get some aliases
+ Element<std::string>& _scxml = interpreter->_scxml;
+ NameSpaceInfo& _nsInfo = interpreter->_nsInfo;
+ Factory* _factory = interpreter->_factory;
+ DataModel& _dataModel = interpreter->_dataModel;
+
+
+ std::map<std::string, NodeSet<std::string> > nodeSets;
+ assembleNodeSets(_nsInfo.xmlNSPrefix, _scxml, nodeSets);
+
+
+ NodeSet<std::string> scxmls = nodeSets["scxml"];
+ scxmls.push_back(_scxml);
+
+ NodeSet<std::string> reachable = getReachableStates(_scxml, interpreter, _nsInfo.xmlNSPrefix);
+
+ 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(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::set<std::string> execContentSet;
+ execContentSet.insert("raise");
+ execContentSet.insert("if");
+ execContentSet.insert("elseif");
+ execContentSet.insert("else");
+ execContentSet.insert("foreach");
+ execContentSet.insert("log");
+ execContentSet.insert("send");
+ execContentSet.insert("assign");
+ execContentSet.insert("script");
+ execContentSet.insert("cancel");
+
+ // these are the valid children for elements in the SCXML namespace as per specification
+ std::map<std::string, std::set<std::string> > validChildren;
+ validChildren["scxml"].insert("state");
+ validChildren["scxml"].insert("parallel");
+ validChildren["scxml"].insert("final");
+ validChildren["scxml"].insert("datamodel");
+ validChildren["scxml"].insert("script");
+
+ validChildren["state"].insert("onentry");
+ validChildren["state"].insert("onexit");
+ validChildren["state"].insert("transition");
+ validChildren["state"].insert("initial");
+ validChildren["state"].insert("state");
+ validChildren["state"].insert("parallel");
+ validChildren["state"].insert("final");
+ validChildren["state"].insert("history");
+ validChildren["state"].insert("datamodel");
+ validChildren["state"].insert("invoke");
+
+ validChildren["parallel"].insert("onentry");
+ validChildren["parallel"].insert("onexit");
+ validChildren["parallel"].insert("transition");
+ validChildren["parallel"].insert("state");
+ validChildren["parallel"].insert("parallel");
+ validChildren["parallel"].insert("history");
+ validChildren["parallel"].insert("datamodel");
+ validChildren["parallel"].insert("invoke");
+
+ validChildren["transition"] = execContentSet;
+ validChildren["onentry"] = execContentSet;
+ validChildren["onexit"] = execContentSet;
+ validChildren["finalize"] = execContentSet;
+
+ validChildren["if"] = execContentSet;
+ validChildren["elseif"] = execContentSet;
+ validChildren["else"] = execContentSet;
+ validChildren["foreach"] = execContentSet;
+
+ validChildren["initial"].insert("transition");
+ validChildren["history"].insert("transition");
+
+ validChildren["final"].insert("onentry");
+ validChildren["final"].insert("onexit");
+ validChildren["final"].insert("donedata");
+
+ validChildren["datamodel"].insert("data");
+
+ validChildren["donedata"].insert("content");
+ validChildren["donedata"].insert("param");
+
+ validChildren["send"].insert("content");
+ validChildren["send"].insert("param");
+
+ validChildren["invoke"].insert("content");
+ validChildren["invoke"].insert("param");
+ validChildren["invoke"].insert("finalize");
+
+ // inverse validChildren to validParents
+ std::map<std::string, std::set<std::string> > validParents;
+ for (std::map<std::string, std::set<std::string> >::iterator parentIter = validChildren.begin(); parentIter != validChildren.end(); parentIter++) {
+ for (std::set<std::string>::iterator childIter = parentIter->second.begin(); childIter != parentIter->second.end(); childIter++) {
+ validParents[*childIter].insert(parentIter->first);
+ }
+ }
+
+
+ for (int i = 0; i < allStates.size(); i++) {
+ Element<std::string> state = Element<std::string>(allStates[i]);
+
+ if (InterpreterImpl::isMember(state, finals) && !HAS_ATTR(state, "id")) // id is not required for finals
+ continue;
+
+ // check for existance of id attribute
+ if (!HAS_ATTR(state, "id")) {
+ issues.push_back(InterpreterIssue("State has no 'id' attribute", state, InterpreterIssue::USCXML_ISSUE_FATAL));
+ continue;
+ }
+ std::string stateId = ATTR(state, "id");
+
+ if (!InterpreterImpl::isMember(state, reachable)) {
+ issues.push_back(InterpreterIssue("State with id '" + stateId + "' is unreachable", state, InterpreterIssue::USCXML_ISSUE_FATAL));
+ }
+
+
+
+ // check for uniqueness of id attribute
+ if (seenStates.find(stateId) != seenStates.end()) {
+ issues.push_back(InterpreterIssue("Duplicate state with id '" + stateId + "'", state, InterpreterIssue::USCXML_ISSUE_FATAL));
+ continue;
+ }
+ seenStates[ATTR(state, "id")] = state;
+ }
+
+ for (int i = 0; i < transitions.size(); i++) {
+ Element<std::string> transition = Element<std::string>(transitions[i]);
+
+ // check for valid target
+ std::list<std::string> targetIds = InterpreterImpl::tokenizeIdRefs(ATTR(transition, "target"));
+ for (std::list<std::string>::iterator targetIter = targetIds.begin(); targetIter != targetIds.end(); targetIter++) {
+ if (seenStates.find(*targetIter) == seenStates.end()) {
+ issues.push_back(InterpreterIssue("Transition has non-existant target state with id '" + *targetIter + "'", transition, InterpreterIssue::USCXML_ISSUE_FATAL));
+ continue;
+ }
+ }
+ }
+
+ // check for redundancy of transition
+ for (int i = 0; i < allStates.size(); i++) {
+ Element<std::string> state = Element<std::string>(allStates[i]);
+ NodeSet<std::string> transitions = InterpreterImpl::filterChildElements(_nsInfo.xmlNSPrefix + "transition", state, false);
+
+ transitions.to_document_order();
+
+ for (int j = 1; j < transitions.size(); j++) {
+ Element<std::string> transition = Element<std::string>(transitions[j]);
+ for (int k = 0; k < j; k++) {
+ Element<std::string> earlierTransition = Element<std::string>(transitions[k]);
+
+ // will the earlier transition always be enabled when the later is?
+ if (!HAS_ATTR(earlierTransition, "cond")) {
+ // earlier transition has no condition -> check event descriptor
+ if (!HAS_ATTR(earlierTransition, "event")) {
+ // earlier transition is eventless
+ issues.push_back(InterpreterIssue("Transition can never be optimally enabled", transition, InterpreterIssue::USCXML_ISSUE_INFO));
+ goto NEXT_TRANSITION;
+
+ } else if (HAS_ATTR(transition, "event")) {
+ // does the earlier transition match all our events?
+ std::list<std::string> events = InterpreterImpl::tokenizeIdRefs(ATTR(transition, "event"));
+
+ bool allMatched = true;
+ for (std::list<std::string>::iterator eventIter = events.begin(); eventIter != events.end(); eventIter++) {
+ if (!InterpreterImpl::nameMatch(ATTR(earlierTransition, "event"), *eventIter)) {
+ allMatched = false;
+ break;
+ }
+ }
+
+ if (allMatched) {
+ issues.push_back(InterpreterIssue("Transition can never be optimally enabled", transition, InterpreterIssue::USCXML_ISSUE_INFO));
+ goto NEXT_TRANSITION;
+ }
+ }
+ }
+ }
+ NEXT_TRANSITION:;
+ }
+ }
+
+ // check for valid initial attribute
+ {
+ NodeSet<std::string> withInitialAttr;
+ withInitialAttr.push_back(allStates);
+ withInitialAttr.push_back(_scxml);
+
+ for (int i = 0; i < withInitialAttr.size(); i++) {
+ Element<std::string> state = Element<std::string>(withInitialAttr[i]);
+ if (HAS_ATTR(state, "initial")) {
+ std::list<std::string> intials = InterpreterImpl::tokenizeIdRefs(ATTR(state, "initial"));
+ for (std::list<std::string>::iterator initIter = intials.begin(); initIter != intials.end(); initIter++) {
+ if (seenStates.find(*initIter) == seenStates.end()) {
+ issues.push_back(InterpreterIssue("Initial attribute has invalid target state with id '" + *initIter + "'", state, InterpreterIssue::USCXML_ISSUE_FATAL));
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ // check that all invokers exists
+ {
+ for (int i = 0; i < invokes.size(); i++) {
+ Element<std::string> invoke = Element<std::string>(invokes[i]);
+ if (HAS_ATTR(invoke, "type") && !_factory->hasInvoker(ATTR(invoke, "type"))) {
+ issues.push_back(InterpreterIssue("Invoke with unknown type '" + ATTR(invoke, "type") + "'", invoke, InterpreterIssue::USCXML_ISSUE_FATAL));
+ continue;
+ }
+ }
+ }
+
+ // check that all io processors exists
+ {
+ for (int i = 0; i < sends.size(); i++) {
+ Element<std::string> send = Element<std::string>(sends[i]);
+ if (HAS_ATTR(send, "type") && !_factory->hasIOProcessor(ATTR(send, "type"))) {
+ issues.push_back(InterpreterIssue("Send to unknown IO Processor '" + ATTR(send, "type") + "'", send, InterpreterIssue::USCXML_ISSUE_FATAL));
+ continue;
+ }
+ }
+ }
+
+ // 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 (int i = 0; i < allExecContentContainers.size(); i++) {
+ Element<std::string> block = Element<std::string>(allExecContentContainers[i]);
+ NodeSet<std::string> execContents = InterpreterImpl::filterChildType(Node_base::ELEMENT_NODE, block);
+ for (int j = 0; j < execContents.size(); j++) {
+ Element<std::string> execContent = Element<std::string>(execContents[j]);
+ // SCXML specific executable content, always available
+ if (InterpreterImpl::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));
+ continue;
+ }
+ }
+ }
+ }
+
+ // check that all SCXML elements have valid parents
+ {
+ for (int i = 0; i < allElements.size(); i++) {
+ Element<std::string> element = Element<std::string>(allElements[i]);
+ std::string localName = LOCALNAME(element);
+ if (!element.getParentNode() || element.getParentNode().getNodeType() != Node_base::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());
+ std::string parentName = LOCALNAME(parent);
+
+ if (validParents[localName].find(parentName) == validParents[localName].end()) {
+ issues.push_back(InterpreterIssue("Element " + localName + " can be no child of " + parentName, element, InterpreterIssue::USCXML_ISSUE_WARNING));
+ continue;
+ }
+ }
+ }
+
+
+ // check that the datamodel is known
+ if (HAS_ATTR(_scxml, "datamodel")) {
+ if (!_factory->hasDataModel(ATTR(_scxml, "datamodel"))) {
+ issues.push_back(InterpreterIssue("SCXML document requires unknown datamodel '" + ATTR(_scxml, "datamodel") + "'", _scxml, InterpreterIssue::USCXML_ISSUE_FATAL));
+
+ // we cannot even check the rest as we require a datamodel
+ return issues;
+ }
+ }
+
+ bool instantiatedDataModel = false;
+ // instantiate datamodel if not explicitly set
+ if (!_dataModel) {
+ if (HAS_ATTR(_scxml, "datamodel")) {
+ // might throw
+ _dataModel = _factory->createDataModel(ATTR(_scxml, "datamodel"), interpreter);
+ instantiatedDataModel = true;
+ } else {
+ _dataModel = _factory->createDataModel("null", interpreter);
+ }
+ }
+
+
+ // test all scripts for valid syntax
+ {
+ for (int i = 0; i < scripts.size(); i++) {
+ Element<std::string> script = Element<std::string>(scripts[i]);
+
+ 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();
+ }
+
+ if (!_dataModel.isValidSyntax(scriptContent)) {
+ issues.push_back(InterpreterIssue("Syntax error in script", script, InterpreterIssue::USCXML_ISSUE_WARNING));
+ }
+ }
+ }
+ }
+
+ // 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);
+
+ for (int i = 0; i < withCondAttrs.size(); i++) {
+ Element<std::string> condAttr = Element<std::string>(withCondAttrs[i]);
+ 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));
+ continue;
+ }
+ }
+ }
+ }
+
+ {
+ 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 (int i = 0; i < withExprAttrs.size(); i++) {
+ Element<std::string> withExprAttr = Element<std::string>(withExprAttrs[i]);
+ if (HAS_ATTR(withExprAttr, "expr")) {
+ if (InterpreterImpl::isMember(withExprAttr, datas) || InterpreterImpl::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;
+ }
+ } else {
+ if (!_dataModel.isValidSyntax(ATTR(withExprAttr, "expr"))) {
+ issues.push_back(InterpreterIssue("Syntax error in expr attribute", withExprAttr, InterpreterIssue::USCXML_ISSUE_WARNING));
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ {
+ for (int i = 0; i < foreachs.size(); i++) {
+ Element<std::string> foreach = Element<std::string>(foreachs[i]);
+ 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));
+ }
+ }
+ if (HAS_ATTR(foreach, "item")) {
+ if (!_dataModel.isValidSyntax(ATTR(foreach, "item"))) {
+ issues.push_back(InterpreterIssue("Syntax error in item attribute", foreach, InterpreterIssue::USCXML_ISSUE_WARNING));
+ }
+ }
+ if (HAS_ATTR(foreach, "index")) {
+ if (!_dataModel.isValidSyntax(ATTR(foreach, "index"))) {
+ issues.push_back(InterpreterIssue("Syntax error in index attribute", foreach, InterpreterIssue::USCXML_ISSUE_WARNING));
+ }
+ }
+ }
+ }
+
+ {
+ for (int i = 0; i < sends.size(); i++) {
+ Element<std::string> send = Element<std::string>(sends[i]);
+ 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));
+ }
+ }
+ if (HAS_ATTR(send, "targetexpr")) {
+ if (!_dataModel.isValidSyntax(ATTR(send, "targetexpr"))) {
+ issues.push_back(InterpreterIssue("Syntax error in targetexpr attribute", send, InterpreterIssue::USCXML_ISSUE_WARNING));
+ }
+ }
+ if (HAS_ATTR(send, "typeexpr")) {
+ if (!_dataModel.isValidSyntax(ATTR(send, "typeexpr"))) {
+ issues.push_back(InterpreterIssue("Syntax error in typeexpr attribute", send, InterpreterIssue::USCXML_ISSUE_WARNING));
+ }
+ }
+ if (HAS_ATTR(send, "idlocation")) {
+ if (!_dataModel.isValidSyntax(ATTR(send, "idlocation"))) {
+ issues.push_back(InterpreterIssue("Syntax error in idlocation attribute", send, InterpreterIssue::USCXML_ISSUE_WARNING));
+ }
+ }
+ if (HAS_ATTR(send, "delayexpr")) {
+ if (!_dataModel.isValidSyntax(ATTR(send, "delayexpr"))) {
+ issues.push_back(InterpreterIssue("Syntax error in delayexpr attribute", send, InterpreterIssue::USCXML_ISSUE_WARNING));
+ }
+ }
+ }
+
+ }
+
+ {
+ for (int i = 0; i < invokes.size(); i++) {
+ Element<std::string> invoke = Element<std::string>(invokes[i]);
+ 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));
+ continue;
+ }
+ }
+ if (HAS_ATTR(invoke, "srcexpr")) {
+ if (!_dataModel.isValidSyntax(ATTR(invoke, "srcexpr"))) {
+ issues.push_back(InterpreterIssue("Syntax error in srcexpr attribute", invoke, InterpreterIssue::USCXML_ISSUE_WARNING));
+ continue;
+ }
+ }
+ if (HAS_ATTR(invoke, "idlocation")) {
+ if (!_dataModel.isValidSyntax(ATTR(invoke, "idlocation"))) {
+ issues.push_back(InterpreterIssue("Syntax error in idlocation attribute", invoke, InterpreterIssue::USCXML_ISSUE_WARNING));
+ continue;
+ }
+ }
+ }
+ }
+
+ {
+ for (int i = 0; i < cancels.size(); i++) {
+ Element<std::string> cancel = Element<std::string>(cancels[i]);
+ 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));
+ continue;
+ }
+ }
+ }
+ }
+
+ if (instantiatedDataModel)
+ _dataModel = DataModel();
+
+ return issues;
+}
+
+
+} \ No newline at end of file
diff --git a/src/uscxml/debug/InterpreterIssue.h b/src/uscxml/debug/InterpreterIssue.h
new file mode 100644
index 0000000..3949829
--- /dev/null
+++ b/src/uscxml/debug/InterpreterIssue.h
@@ -0,0 +1,54 @@
+/**
+ * @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 INTERPRETERISSUE_H_962CB305
+#define INTERPRETERISSUE_H_962CB305
+
+#include "uscxml/Common.h"
+#include <list>
+#include <DOM/Node.hpp>
+
+namespace uscxml {
+
+class InterpreterImpl;
+
+class USCXML_API InterpreterIssue {
+public:
+ enum IssueSeverity {
+ USCXML_ISSUE_FATAL,
+ USCXML_ISSUE_WARNING,
+ USCXML_ISSUE_INFO
+ };
+
+ InterpreterIssue(const std::string& msg, Arabica::DOM::Node<std::string> node, IssueSeverity severity);
+
+ std::string xPath;
+ std::string message;
+ Arabica::DOM::Node<std::string> node;
+ IssueSeverity severity;
+
+private:
+ static std::list<InterpreterIssue> forInterpreter(InterpreterImpl* interpreter);
+ friend class InterpreterImpl;
+};
+USCXML_API std::ostream& operator<< (std::ostream& os, const InterpreterIssue& issue);
+
+}
+
+#endif /* end of include guard: INTERPRETERISSUE_H_962CB305 */