summaryrefslogtreecommitdiffstats
path: root/src/uscxml
diff options
context:
space:
mode:
authorStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2014-12-07 14:31:09 (GMT)
committerStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2014-12-07 14:31:09 (GMT)
commit9608216597fd17021d38e80689644beb3c85abb9 (patch)
tree1e3375c8beecd160be1f66750c201167c1b8c6a0 /src/uscxml
parent93f8cb376e6e496f0194d1e0657081354434da96 (diff)
downloaduscxml-9608216597fd17021d38e80689644beb3c85abb9.zip
uscxml-9608216597fd17021d38e80689644beb3c85abb9.tar.gz
uscxml-9608216597fd17021d38e80689644beb3c85abb9.tar.bz2
More complete xinclude implementation and tests
Diffstat (limited to 'src/uscxml')
-rw-r--r--src/uscxml/Interpreter.cpp149
-rw-r--r--src/uscxml/Interpreter.h1
-rw-r--r--src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp10
-rw-r--r--src/uscxml/plugins/datamodel/promela/PromelaParser.cpp11
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;
}
}