diff options
Diffstat (limited to 'src/uscxml/Interpreter.cpp')
-rw-r--r-- | src/uscxml/Interpreter.cpp | 519 |
1 files changed, 11 insertions, 508 deletions
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; } |