diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2014-12-07 14:31:09 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2014-12-07 14:31:09 (GMT) |
commit | 9608216597fd17021d38e80689644beb3c85abb9 (patch) | |
tree | 1e3375c8beecd160be1f66750c201167c1b8c6a0 /src | |
parent | 93f8cb376e6e496f0194d1e0657081354434da96 (diff) | |
download | uscxml-9608216597fd17021d38e80689644beb3c85abb9.zip uscxml-9608216597fd17021d38e80689644beb3c85abb9.tar.gz uscxml-9608216597fd17021d38e80689644beb3c85abb9.tar.bz2 |
More complete xinclude implementation and tests
Diffstat (limited to 'src')
-rw-r--r-- | src/uscxml/Interpreter.cpp | 149 | ||||
-rw-r--r-- | src/uscxml/Interpreter.h | 1 | ||||
-rw-r--r-- | src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp | 10 | ||||
-rw-r--r-- | src/uscxml/plugins/datamodel/promela/PromelaParser.cpp | 11 |
4 files changed, 148 insertions, 23 deletions
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index cb780df..3d068f6 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -1148,6 +1148,7 @@ bool InterpreterImpl::runOnMainThread(int fps, bool blocking) { tthread::this_thread::sleep_for(tthread::chrono::milliseconds(nextRun - tthread::timeStamp())); } } else { + _lastRunOnMainThread = tthread::timeStamp(); return true; } } @@ -1246,7 +1247,6 @@ void InterpreterImpl::setupDOM() { _xpath.setNamespaceContext(*_nsInfo.getNSContext()); // normalize document - // TODO: Resolve XML includes #if 0 // make sure every state has an id @@ -1288,30 +1288,139 @@ void InterpreterImpl::setupDOM() { void InterpreterImpl::resolveXIncludes() { std::string xIncludeNS = _nsInfo.getXMLPrefixForNS("http://www.w3.org/2001/XInclude"); - if (xIncludeNS.size() > 0) { - Arabica::XPath::NodeSet<std::string> xincludes = _xpath.evaluate("//" + xIncludeNS + "include", _document.getDocumentElement()).asNodeSet(); - for (int i = 0; i < xincludes.size(); i++) { - if (HAS_ATTR_CAST(xincludes[i], "href")) { - URL src(ATTR_CAST(xincludes[i], "href")); - if (!src.isAbsolute()) { - if (!src.toAbsolute(_baseURI)) { - LOG(ERROR) << "Cannot resolve relative URL '" << ATTR_CAST(xincludes[i], "href") << "' to absolute URL via base URL '" << _baseURI << "'"; - continue; - } + + // no element in namespace for xinclude, don't bother searching + if (xIncludeNS.size() == 0) + return; + + std::map<std::string, std::string> mergedNs = _nsInfo.nsInfo; + + std::list<std::string> includeChain; + includeChain.push_back(_sourceURI); + + Arabica::XPath::NodeSet<std::string> xincludes = _xpath.evaluate("//" + xIncludeNS + "include", _document.getDocumentElement()).asNodeSet(); + for (int i = 0; i < xincludes.size(); i++) { + // recursively resolve includes + resolveXIncludes(includeChain, mergedNs, xIncludeNS, Element<std::string>(xincludes[i])); + } + + // update NameSpaceInfo and reinit xpath resolver + _nsInfo = NameSpaceInfo(mergedNs); + _xpath.setNamespaceContext(*_nsInfo.getNSContext()); + +} + +void InterpreterImpl::resolveXIncludes(std::list<std::string> includeChain, std::map<std::string, std::string>& mergedNS, const std::string& xIncludeNS, Arabica::DOM::Element<std::string> xinclude) { + NodeSet<std::string> newNodes; + if (HAS_ATTR(xinclude, "href")) { + URL src(ATTR(xinclude, "href")); + if (!src.isAbsolute()) { + if (!src.toAbsolute(_baseURI)) { + LOG(ERROR) << "Cannot resolve relative URL '" << ATTR(xinclude, "href") << "' to absolute URL via base URL '" << _baseURI << "', trying xi:fallback"; + goto TRY_WITH_FALLBACK; + } + } + + if (std::find(includeChain.begin(), includeChain.end(), src.asString()) != includeChain.end()) { + std::stringstream incErr; + incErr << ("Ignoring recursive inclusion of '" + src.asString() + " via:") << std::endl; + for (std::list<std::string>::iterator incIter = includeChain.begin(); incIter != includeChain.end(); incIter++) { + incErr << " " << *incIter << std::endl; + } + LOG(ERROR) << incErr.str(); + return; + } + includeChain.push_back(src.asString()); + + if (HAS_ATTR(xinclude, "accept")) { + src.addOutHeader("Accept", ATTR_CAST(xinclude, "accept")); + } + + if (HAS_ATTR(xinclude, "accept-language")) { + src.addOutHeader("Accept-Language", ATTR_CAST(xinclude, "accept-language")); + } + + std::string includedContent; + try { + includedContent = src.getInContent(); + } catch (Event e) { + goto TRY_WITH_FALLBACK; + } + + if (HAS_ATTR(xinclude, "parse") && iequals(ATTR(xinclude, "parse"), "text")) { + // parse as text + Text<std::string> textNode = _document.createTextNode(includedContent); + xinclude.getParentNode().insertBefore(textNode, xinclude); + goto REMOVE_AND_RECURSE; + } else { + // parse as XML + NameSpacingParser xiParser = NameSpacingParser::fromXML(includedContent); + if (xiParser.errorsReported()) { + ERROR_PLATFORM_THROW(xiParser.errors()); + } else { + // scxml namespace prefixed and non-profixed + if (mergedNS.find("http://www.w3.org/2005/07/scxml") != mergedNS.end() && xiParser.nameSpace.find("http://www.w3.org/2005/07/scxml") == xiParser.nameSpace.end()) { + LOG(WARNING) << ("Warning for '" + DOMUtils::xPathForNode(xinclude) + "': root document maps SCXML namespace to prefix '" + mergedNS["http://www.w3.org/2005/07/scxml"] + "', included document does not specify SCXML namespace at all"); } - NameSpacingParser xiParser = NameSpacingParser::fromXML(src.getInContent()); - if (xiParser.errorsReported()) { - ERROR_PLATFORM_THROW(xiParser.errors()); - } else { - // import DOM - Node<std::string> imported = _document.importNode(xiParser.getDocument().getDocumentElement(), true); - xincludes[i].getParentNode().insertBefore(imported, xincludes[i]); - xincludes[i].getParentNode().removeChild(xincludes[i]); + if (mergedNS.find("http://www.w3.org/2005/07/scxml") == mergedNS.end() && xiParser.nameSpace.find("http://www.w3.org/2005/07/scxml") != xiParser.nameSpace.end()) { + LOG(ERROR) << ("Error for '" + DOMUtils::xPathForNode(xinclude) + "': root document uses implicit SCXML namespace without prefix, included document does map it to prefix '" + xiParser.nameSpace["http://www.w3.org/2005/07/scxml"] + "', trying xi:fallback"); + goto TRY_WITH_FALLBACK; + + } + + // merge namespaces to prefix mappings + for (std::map<std::string, std::string>::iterator nsIter = xiParser.nameSpace.begin(); nsIter != xiParser.nameSpace.end(); nsIter++) { + + // same nsURL but different prefix + if (mergedNS.find(nsIter->first) != mergedNS.end() && mergedNS[nsIter->first] != nsIter->second) { + LOG(ERROR) << ("Error for '" + DOMUtils::xPathForNode(xinclude) + "': Cannot map namespace '" + nsIter->first + "' to prefix '" + nsIter->second + "', it is already mapped to prefix '" + mergedNS[nsIter->first] + "', trying xi:fallback"); + goto TRY_WITH_FALLBACK; + } + + // same prefix but different nsURL + for (std::map<std::string, std::string>::iterator currIter = mergedNS.begin(); currIter != mergedNS.end(); currIter++) { + if (currIter->second == nsIter->second && currIter->first != nsIter->first) { + LOG(ERROR) << ("Error for '" + DOMUtils::xPathForNode(xinclude) + "': Cannot assign prefix '" + nsIter->second + "' to namespace '" + nsIter->first + "' it is already a prefix for '" + currIter->first + "', trying xi:fallback"); + goto TRY_WITH_FALLBACK; + } + } + mergedNS[nsIter->first] = nsIter->second; } + + // import DOM + Node<std::string> imported = _document.importNode(xiParser.getDocument().getDocumentElement(), true); + newNodes.push_back(imported); + xinclude.getParentNode().insertBefore(imported, xinclude); + goto REMOVE_AND_RECURSE; } } + } else { + LOG(ERROR) << "No href attribute for xi:xinclude at '" << DOMUtils::xPathForNode(xinclude) << "', trying xi:fallback"; + goto TRY_WITH_FALLBACK; + } +TRY_WITH_FALLBACK: + { + NodeSet<std::string> fallbacks = filterChildElements(xIncludeNS + "fallback", xinclude); + if (fallbacks.size() > 0) { + LOG(WARNING) << "Using xi:fallback for '" << DOMUtils::xPathForNode(xinclude) << "'"; + // move the fallbacks children in place + NodeList<std::string> fallbackChildren = fallbacks[0].getChildNodes(); + while (fallbacks[0].getChildNodes().getLength() > 0) { + newNodes.push_back(fallbackChildren.item(0)); + xinclude.getParentNode().insertBefore(fallbackChildren.item(0), xinclude); + } + } else { + LOG(WARNING) << "No xi:fallback found for '" << DOMUtils::xPathForNode(xinclude) << "', document most likely incomplete"; + } + } +REMOVE_AND_RECURSE: + xinclude.getParentNode().removeChild(xinclude); + for (int i = 0; i < newNodes.size(); i++) { + Arabica::XPath::NodeSet<std::string> xincludes = filterChildElements(xIncludeNS + "include", newNodes[i], true); + for (int j = 0; j < xincludes.size(); j++) { + resolveXIncludes(includeChain, mergedNS, xIncludeNS, Element<std::string>(xincludes[j])); + } } - } void InterpreterImpl::init() { diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index d962083..12582cf 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -459,6 +459,7 @@ protected: void init(); void setupDOM(); void resolveXIncludes(); + void resolveXIncludes(std::list<std::string> includeChain, std::map<std::string, std::string>& mergedNS, const std::string& xIncludeNS, Arabica::DOM::Element<std::string> xinclude); virtual void setupIOProcessors(); std::list<InterpreterIssue> validate(); diff --git a/src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp b/src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp index d63c354..5b3c79c 100644 --- a/src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp +++ b/src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp @@ -375,8 +375,14 @@ void PromelaDataModel::evaluateDecl(void* ast) { std::list<PromelaParserNode*>::iterator opIterAsgn = (*nameIter)->operands.begin(); PromelaParserNode* name = *opIterAsgn++; PromelaParserNode* expr = *opIterAsgn++; - - variable.compound["value"] = evaluateExpr(expr); + + try { + variable.compound["value"] = evaluateExpr(expr); + } catch(uscxml::Event e) { + // test277, declare and throw + _variables.compound[name->value] = variable; + throw e; + } assert(opIterAsgn == (*nameIter)->operands.end()); _variables.compound[name->value] = variable; diff --git a/src/uscxml/plugins/datamodel/promela/PromelaParser.cpp b/src/uscxml/plugins/datamodel/promela/PromelaParser.cpp index ada2c48..74cb791 100644 --- a/src/uscxml/plugins/datamodel/promela/PromelaParser.cpp +++ b/src/uscxml/plugins/datamodel/promela/PromelaParser.cpp @@ -38,6 +38,15 @@ void promela_error (void* yylloc_param, uscxml::PromelaParser* ctx, void* yyscan PROMELA_LTYPE* yylloc = (PROMELA_LTYPE*)yylloc_param; // mark as pending exception as we cannot throw from constructor and have the destructor called ERROR_EXECUTION(excEvent, err); + excEvent.data.compound["line"] = uscxml::Data(yylloc->first_line, uscxml::Data::VERBATIM); + excEvent.data.compound["col"] = uscxml::Data(yylloc->first_column, uscxml::Data::VERBATIM); + + std::stringstream ssUnderline; + for (int i = 0; i < yylloc->first_column; i++) + ssUnderline << " "; + ssUnderline << "^"; + excEvent.data.compound["sourcemark"] = uscxml::Data(ssUnderline.str(), uscxml::Data::VERBATIM); + ctx->pendingException = excEvent; } @@ -85,7 +94,7 @@ void PromelaParser::init(const std::string& expr) { if (pendingException.name.size() > 0) { // parsing failed in promela_error destroy(); - pendingException.data.compound["code"] = Data(expr, Data::VERBATIM); + pendingException.data.compound["sourceline"] = Data(expr, Data::VERBATIM); throw pendingException; } } |