summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt35
-rw-r--r--README.md8
-rw-r--r--contrib/ctest/CTestCustom.ctest.in21
-rw-r--r--src/bindings/swig/php/uscxmlNativePHP.php62
-rw-r--r--src/uscxml/Factory.cpp12
-rw-r--r--src/uscxml/Factory.h61
-rw-r--r--src/uscxml/Interpreter.cpp52
-rw-r--r--src/uscxml/Interpreter.h2
-rw-r--r--src/uscxml/interpreter/InterpreterDraft6.cpp11
-rw-r--r--src/uscxml/interpreter/InterpreterDraft7.cpp18
-rw-r--r--src/uscxml/interpreter/InterpreterDraft7.h2
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp14
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h30
-rw-r--r--src/uscxml/plugins/datamodel/null/NULLDataModel.cpp121
-rw-r--r--src/uscxml/plugins/datamodel/null/NULLDataModel.h77
-rw-r--r--src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp470
-rw-r--r--src/uscxml/plugins/datamodel/xpath/XPathDataModel.h143
-rw-r--r--test/CMakeLists.txt6
-rw-r--r--test/samples/w3c/ecma/test463.scxml2
-rw-r--r--test/src/test-arabica-xpath.cpp198
20 files changed, 1270 insertions, 75 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 87f6ef8..fd72602 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -659,6 +659,39 @@ if (APPLE AND IOS AND OFF)
endif()
+
+# NULL datamodel
+
+file(GLOB NULL_DATAMODEL
+ src/uscxml/plugins/datamodel/null/*.cpp
+ src/uscxml/plugins/datamodel/null/*.h
+)
+source_group("Datamodel\\null" FILES ${NULL_DATAMODEL})
+if (BUILD_AS_PLUGINS)
+ add_library(datamodel_null SHARED ${NULL_DATAMODEL})
+ target_link_libraries(datamodel_null uscxml)
+ set_target_properties(datamodel_null PROPERTIES FOLDER "Plugin DataModel")
+else()
+ list (APPEND USCXML_FILES ${NULL_DATAMODEL})
+endif()
+
+
+# XPath datamodel
+
+file(GLOB XPATH_DATAMODEL
+ src/uscxml/plugins/datamodel/xpath/*.cpp
+ src/uscxml/plugins/datamodel/xpath/*.h
+)
+source_group("Datamodel\\xpath" FILES ${XPATH_DATAMODEL})
+if (BUILD_AS_PLUGINS)
+ add_library(datamodel_xpath SHARED ${XPATH_DATAMODEL})
+ target_link_libraries(datamodel_xpath uscxml)
+ set_target_properties(datamodel_xpath PROPERTIES FOLDER "Plugin DataModel")
+else()
+ list (APPEND USCXML_FILES ${XPATH_DATAMODEL})
+endif()
+
+
# GOOGLE V8 ecmascript datamodel
#if (NOT APPLE OR IOS)
@@ -759,8 +792,8 @@ if (UMUNDO_FOUND)
list (APPEND USCXML_FILES ${UMUNDO_INVOKER})
list (APPEND USCXML_OPT_LIBS ${UMUNDO_LIBRARIES})
endif()
+ add_definitions("-DUMUNDO_STATIC")
endif()
-add_definitions("-DUMUNDO_STATIC")
# USCXML invoker
diff --git a/README.md b/README.md
index 476d14b..05d2017 100644
--- a/README.md
+++ b/README.md
@@ -9,8 +9,8 @@ the respective build-process and did not precompile required libraries.
* <b>Datamodels</b>
* ECMAScript using Google's v8 and JavaScriptCore (JSC is incomplete)
* Prolog using SWI prolog
- * No NULL datamodel yet
- * No XPath datamodel yet
+ * NULL datamodel with required <tt>In</tt> predicate
+ * <b>No</b> XPath datamodel yet
* <b>Invokers</b>
* <tt>scxml</tt>: Invoke a nested scxml interpreter
* <tt>dirmon</tt>: Watches a directory for changes to files
@@ -23,7 +23,7 @@ the respective build-process and did not precompile required libraries.
* <b>Communication</b>
* Features the standard basichttp io-processor
* Features the required SCXML io-processor
- * No DOM io-processor
+ * <b>No</b> DOM io-processor yet
* Can actually respond to HTTP requests with data via &lt;response>
* <b>Language Bindings</b>
* PHP module for apache and cli interpreter
@@ -33,7 +33,7 @@ the respective build-process and did not precompile required libraries.
We continuously run the [W3C IRP tests](http://www.w3.org/Voice/2013/scxml-irp/) for SCXML. The results the for
various platforms can be [found here](http://uscxml.tk.informatik.tu-darmstadt.de/cdash/index.php?project=uscxml).
There are a few [excluded tests](https://github.com/tklab-tud/uscxml/blob/master/contrib/ctest/CTestCustom.ctest.in)
-regarding the <tt>NULL</tt> and <tt>XPath</tt> datamodel, as well as the manual tests.
+regarding the <tt>XPath</tt> datamodel and the manual tests.
uSCXML still fails the following tests:
diff --git a/contrib/ctest/CTestCustom.ctest.in b/contrib/ctest/CTestCustom.ctest.in
index 480cb18..fd93ab6 100644
--- a/contrib/ctest/CTestCustom.ctest.in
+++ b/contrib/ctest/CTestCustom.ctest.in
@@ -8,25 +8,6 @@
# skip xpath datamodel tests
set(CTEST_CUSTOM_TESTS_IGNORE
- "test463.scxml"
- "test464.scxml"
- "test465.scxml"
- "test466.scxml"
- "test467.scxml"
- "test468.scxml"
- "test469.scxml"
- "test470.scxml"
- "test473.scxml"
- "test474.scxml"
- "test475.scxml"
- "test476.scxml"
- "test477.scxml"
- "test478.scxml"
- "test479.scxml"
- "test480.scxml"
- "test481.scxml"
- "test482.scxml"
- "test537.scxml"
"test539.scxml"
"test540.scxml"
"test542.scxml"
@@ -35,13 +16,13 @@ set(CTEST_CUSTOM_TESTS_IGNORE
"test547.scxml"
"test555.scxml"
"test568.scxml"
+
"test178.scxml"
"test230.scxml"
"test250.scxml"
"test307.scxml"
"test313.scxml"
"test314.scxml"
- "test436.scxml"
)
diff --git a/src/bindings/swig/php/uscxmlNativePHP.php b/src/bindings/swig/php/uscxmlNativePHP.php
index 34984ab..89cad82 100644
--- a/src/bindings/swig/php/uscxmlNativePHP.php
+++ b/src/bindings/swig/php/uscxmlNativePHP.php
@@ -26,6 +26,64 @@ if (!extension_loaded('uscxmlNativePHP')) {
+abstract class uscxmlNativePHP {
+ const CAN_NOTHING = 0;
+
+ const CAN_BASIC_HTTP = 1;
+
+ const CAN_GENERIC_HTTP = 2;
+
+ const ANY = 0;
+
+ const BOOL = BOOL;
+
+ const NUMBER = NUMBER;
+
+ const STRING = STRING;
+
+ const NODE_SET = NODE_SET;
+
+ static function NaN_get() {
+ return NaN_get();
+ }
+
+ static function Zero_get() {
+ return Zero_get();
+ }
+
+ static function Negative_Zero_get() {
+ return Negative_Zero_get();
+ }
+
+ static function Infinity_get() {
+ return Infinity_get();
+ }
+
+ static function Negative_Infinity_get() {
+ return Negative_Infinity_get();
+ }
+
+ static function isNaN($value) {
+ return isNaN($value);
+ }
+
+ static function isInfinity($value) {
+ return isInfinity($value);
+ }
+
+ static function isNegativeInfinity($value) {
+ return isNegativeInfinity($value);
+ }
+
+ static function isInfinite($value) {
+ return isInfinite($value);
+ }
+
+ static function roundNumber($value) {
+ return roundNumber($value);
+ }
+}
+
/* PHP Proxy Classes */
class Data {
public $_cPtr=null;
@@ -67,6 +125,10 @@ class Data {
}
}
+ function isValid() {
+ return Data_isValid($this->_cPtr);
+ }
+
static function fromJSON($jsonString) {
$r=Data_fromJSON($jsonString);
if (is_resource($r)) {
diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp
index e927693..c74196a 100644
--- a/src/uscxml/Factory.cpp
+++ b/src/uscxml/Factory.cpp
@@ -47,6 +47,10 @@
# include "uscxml/plugins/datamodel/prolog/swi/SWIDataModel.h"
# endif
+#include "uscxml/plugins/datamodel/null/NULLDataModel.h"
+#include "uscxml/plugins/datamodel/xpath/XPathDataModel.h"
+
+
# include "uscxml/plugins/element/fetch/FetchElement.h"
# include "uscxml/plugins/element/response/ResponseElement.h"
# include "uscxml/plugins/element/postpone/PostponeElement.h"
@@ -146,6 +150,14 @@ Factory::Factory() {
// these are always available
{
+ NULLDataModel* dataModel = new NULLDataModel();
+ registerDataModel(dataModel);
+ }
+ {
+ XPathDataModel* dataModel = new XPathDataModel();
+ registerDataModel(dataModel);
+ }
+ {
USCXMLInvoker* invoker = new USCXMLInvoker();
registerInvoker(invoker);
}
diff --git a/src/uscxml/Factory.h b/src/uscxml/Factory.h
index c396e63..540cf61 100644
--- a/src/uscxml/Factory.h
+++ b/src/uscxml/Factory.h
@@ -240,14 +240,33 @@ public:
virtual void pushContext() = 0;
virtual void popContext() = 0;
+ virtual bool supportsJSON() { return false; }
+
virtual void eval(const std::string& expr) = 0;
virtual std::string evalAsString(const std::string& expr) = 0;
virtual bool evalAsBool(const std::string& expr) = 0;
- virtual void assign(const std::string& location, const Arabica::DOM::Document<std::string>& doc) = 0;
- virtual void assign(const std::string& location, const std::string& expr) = 0;
- virtual void assign(const std::string& location, const Data& data) = 0;
virtual bool isDeclared(const std::string& expr) = 0;
+ virtual void assign(const std::string& location,
+ const Arabica::DOM::Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& assignElem) = 0;
+ virtual void assign(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& assignElem) = 0;
+ virtual void assign(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& assignElem) = 0;
+
+ virtual void init(const std::string& location,
+ const Arabica::DOM::Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& dataElem) = 0;
+ virtual void init(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& dataElem) = 0;
+ virtual void init(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& dataElem) = 0;
+
protected:
InterpreterImpl* _interpreter;
};
@@ -295,6 +314,9 @@ public:
virtual void popContext() {
return _impl->popContext();
}
+ virtual bool supportsJSON() {
+ return _impl->supportsJSON();
+ }
virtual void eval(const std::string& expr) {
return _impl->eval(expr);
@@ -306,15 +328,36 @@ public:
return _impl->evalAsBool(expr);
}
- virtual void assign(const std::string& location, const Arabica::DOM::Document<std::string>& doc) {
- return _impl->assign(location, doc);
+ virtual void assign(const std::string& location,
+ const Arabica::DOM::Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& assignElem) {
+ return _impl->assign(location, doc, assignElem);
+ }
+ virtual void assign(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& assignElem) {
+ return _impl->assign(location, expr, assignElem);
+ }
+ virtual void assign(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& assignElem) {
+ return _impl->assign(location, data, assignElem);
}
- virtual void assign(const std::string& location, const std::string& expr) {
- return _impl->assign(location, expr);
+ virtual void init(const std::string& location,
+ const Arabica::DOM::Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& dataElem) {
+ return _impl->init(location, doc, dataElem);
+ }
+ virtual void init(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& dataElem) {
+ return _impl->init(location, expr, dataElem);
}
- virtual void assign(const std::string& location, const Data& data) {
- return _impl->assign(location, data);
+ virtual void init(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& dataElem) {
+ return _impl->init(location, data, dataElem);
}
virtual bool isDeclared(const std::string& expr) {
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index 33d3b03..a78d4a0 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -313,7 +313,7 @@ void InterpreterImpl::init() {
/**
* Called with a single data element from the topmost datamodel element.
*/
-void InterpreterImpl::initializeData(const Node<std::string>& data) {
+void InterpreterImpl::initializeData(const Element<std::string>& data) {
if (!_dataModel) {
LOG(ERROR) << "Cannot initialize data when no datamodel is given!";
return;
@@ -327,7 +327,7 @@ void InterpreterImpl::initializeData(const Node<std::string>& data) {
/// test 240 - initialize from invoke request
if (_invokeReq.params.find(ATTR(data, "id")) != _invokeReq.params.end()) {
try {
- _dataModel.assign(ATTR(data, "id"), _invokeReq.params.find(ATTR(data, "id"))->second);
+ _dataModel.init(ATTR(data, "id"), _invokeReq.params.find(ATTR(data, "id"))->second, data);
} catch (Event e) {
LOG(ERROR) << "Syntax error when initializing data from parameters:" << std::endl << e << std::endl;
receiveInternal(e);
@@ -336,7 +336,7 @@ void InterpreterImpl::initializeData(const Node<std::string>& data) {
}
if (_invokeReq.namelist.find(ATTR(data, "id")) != _invokeReq.namelist.end()) {
try {
- _dataModel.assign(ATTR(data, "id"), _invokeReq.namelist.find(ATTR(data, "id"))->second);
+ _dataModel.init(ATTR(data, "id"), _invokeReq.namelist.find(ATTR(data, "id"))->second, data);
} catch (Event e) {
LOG(ERROR) << "Syntax error when initializing data from namelist:" << std::endl << e << std::endl;
receiveInternal(e);
@@ -350,13 +350,13 @@ void InterpreterImpl::initializeData(const Node<std::string>& data) {
// expression given directly
std::string value = ATTR(data, "expr");
try {
- _dataModel.assign(ATTR(data, "id"), value);
+ _dataModel.init(ATTR(data, "id"), value, data);
} catch (Event e) {
LOG(ERROR) << "Syntax error in data element:" << std::endl << e << std::endl;
/// test 277
/// todo: if the identifier is invalid we'll raise to error events
receiveInternal(e);
- _dataModel.assign(ATTR(data, "id"), "undefined");
+ _dataModel.init(ATTR(data, "id"), "undefined", data);
}
return;
}
@@ -385,12 +385,12 @@ void InterpreterImpl::initializeData(const Node<std::string>& data) {
Arabica::SAX2DOM::Parser<std::string> parser;
if(parser.parse(inputSource) && parser.getDocument()) {
try {
- _dataModel.assign(ATTR(data, "id"), parser.getDocument());
+ _dataModel.init(ATTR(data, "id"), parser.getDocument(), data);
return;
} catch (Event e) {
LOG(ERROR) << "Syntax error in data element:" << std::endl << e << std::endl;
receiveInternal(e);
- _dataModel.assign(ATTR(data, "id"), "undefined");
+ _dataModel.init(ATTR(data, "id"), "undefined", data);
}
return;
}
@@ -416,7 +416,7 @@ void InterpreterImpl::initializeData(const Node<std::string>& data) {
Document<std::string> dom = domFactory.createDocument(contentChild.getNamespaceURI(), "", 0);
Node<std::string> newNode = dom.importNode(contentChild, true);
dom.appendChild(newNode);
- _dataModel.assign(ATTR(data, "id"), dom);
+ _dataModel.init(ATTR(data, "id"), dom, data);
return;
} else if (contentChild) {
// get first child and process below
@@ -428,7 +428,7 @@ void InterpreterImpl::initializeData(const Node<std::string>& data) {
if (contentToProcess.length() > 0) {
/// try to interpret as JSON
try {
- _dataModel.assign(ATTR(data, "id"), contentToProcess);
+ _dataModel.init(ATTR(data, "id"), contentToProcess, data);
} catch(Event e) {
/// create space normalized string if that failed
/// test 558
@@ -443,10 +443,10 @@ void InterpreterImpl::initializeData(const Node<std::string>& data) {
seperator = " ";
}
} while (iss);
- _dataModel.assign(ATTR(data, "id"), Data(spaceNormalized.str(), Data::VERBATIM));
+ _dataModel.init(ATTR(data, "id"), Data(spaceNormalized.str(), Data::VERBATIM), data);
}
} else {
- _dataModel.assign(ATTR(data, "id"), "undefined");
+ _dataModel.init(ATTR(data, "id"), "undefined", data);
}
} catch (Event e) {
@@ -726,7 +726,7 @@ void InterpreterImpl::send(const Arabica::DOM::Node<std::string>& element) {
*/
sendReq.sendid = ATTR(getParentState(element), "id") + "." + getUUID();
if (HAS_ATTR(element, "idlocation") && _dataModel) {
- _dataModel.assign(ATTR(element, "idlocation"), "'" + sendReq.sendid + "'");
+ _dataModel.assign(ATTR(element, "idlocation"), "'" + sendReq.sendid + "'", Element<std::string>());
} else {
sendReq.hideSendId = true;
}
@@ -880,7 +880,7 @@ void InterpreterImpl::invoke(const Arabica::DOM::Node<std::string>& element) {
} else {
invokeReq.invokeid = ATTR(getParentState(element), "id") + "." + getUUID();
if (HAS_ATTR(element, "idlocation") && _dataModel) {
- _dataModel.assign(ATTR(element, "idlocation"), "'" + invokeReq.invokeid + "'");
+ _dataModel.assign(ATTR(element, "idlocation"), "'" + invokeReq.invokeid + "'", Element<std::string>());
}
}
} catch (Event e) {
@@ -941,7 +941,7 @@ void InterpreterImpl::invoke(const Arabica::DOM::Node<std::string>& element) {
}
if (_dataModel) {
try {
- _dataModel.assign("_invokers['" + invokeReq.invokeid + "']", invoker.getDataModelVariables());
+ _dataModel.assign("_invokers['" + invokeReq.invokeid + "']", invoker.getDataModelVariables(), Element<std::string>());
} catch(...) {
LOG(ERROR) << "Exception caught while assigning datamodel variables from invoker " << invokeReq.invokeid;
}
@@ -971,7 +971,7 @@ void InterpreterImpl::cancelInvoke(const Arabica::DOM::Node<std::string>& elemen
LOG(INFO) << "Removed invoker at " << invokeId;
if (_dataModel) {
try {
- _dataModel.assign("_invokers['" + invokeId + "']", "''");
+ _dataModel.assign("_invokers['" + invokeId + "']", "''", Element<std::string>());
} catch (Event e) {
LOG(ERROR) << "Syntax when removing invoker:" << std::endl << e << std::endl;
}
@@ -1139,13 +1139,13 @@ void InterpreterImpl::executeContent(const Arabica::DOM::Node<std::string>& cont
// assign array element to item
std::stringstream ss;
ss << array << "[" << iteration << "]";
- _dataModel.assign(item, ss.str());
+ _dataModel.assign(item, ss.str(), Element<std::string>());
}
if (index.length() > 0) {
// assign iteration element to index
std::stringstream ss;
ss << iteration;
- _dataModel.assign(index,ss.str());
+ _dataModel.assign(index,ss.str(), Element<std::string>());
}
if (content.hasChildNodes())
// execute content and have exception rethrown to break foreach
@@ -1176,14 +1176,28 @@ void InterpreterImpl::executeContent(const Arabica::DOM::Node<std::string>& cont
}
} else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "assign")) {
// --- ASSIGN --------------------------
- if (_dataModel && HAS_ATTR(content, "location") && HAS_ATTR(content, "expr")) {
+ if (_dataModel && HAS_ATTR(content, "location")) {
try {
if (!_dataModel.isDeclared(ATTR(content, "location"))) {
// test 286, 331
LOG(ERROR) << "Assigning to undeclared location '" << ATTR(content, "location") << "' not allowed." << std::endl;
throw Event("error.execution", Event::PLATFORM);
} else {
- _dataModel.assign(ATTR(content, "location"), ATTR(content, "expr"));
+ Data data;
+ Document<std::string> dom;
+ std::string text;
+ processContentElement(content, dom, text, data);
+
+ if (dom) {
+ _dataModel.assign(ATTR(content, "location"), dom, Element<std::string>(content));
+ } else if(data && _dataModel.supportsJSON()) {
+ _dataModel.assign(ATTR(content, "location"), data, Element<std::string>(content));
+ } else if (text.length() > 0) {
+ _dataModel.assign(ATTR(content, "location"), text, Element<std::string>(content));
+ } else {
+ LOG(ERROR) << "Assign element does not specify any content" << std::endl;
+ throw Event("error.execution", Event::PLATFORM);
+ }
}
}
CATCH_AND_DISTRIBUTE("Syntax error in attributes of assign element:")
diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h
index 141d630..673cba1 100644
--- a/src/uscxml/Interpreter.h
+++ b/src/uscxml/Interpreter.h
@@ -222,7 +222,7 @@ protected:
void init();
void normalize(Arabica::DOM::Element<std::string>& scxml);
- void initializeData(const Arabica::DOM::Node<std::string>& data);
+ void initializeData(const Arabica::DOM::Element<std::string>& data);
void setupIOProcessors();
bool _stable;
diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp
index 3c4a699..dfcc457 100644
--- a/src/uscxml/interpreter/InterpreterDraft6.cpp
+++ b/src/uscxml/interpreter/InterpreterDraft6.cpp
@@ -34,7 +34,7 @@ void InterpreterDraft6::interpret() {
}
if (_dataModel) {
- _dataModel.assign("_x.args", _cmdLineOptions);
+ _dataModel.assign("_x.args", _cmdLineOptions, Element<std::string>());
}
setupIOProcessors();
@@ -50,13 +50,15 @@ void InterpreterDraft6::interpret() {
for (unsigned int i = 0; i < dataElems.size(); i++) {
// do not process data elements of nested documents from invokers
if (!getAncestorElement(dataElems[i], _xmlNSPrefix + "invoke"))
- initializeData(dataElems[i]);
+ if (dataElems[i].getNodeType() == Node_base::ELEMENT_NODE)
+ initializeData(Element<std::string>(dataElems[i]));
}
} else if(_dataModel) {
// initialize current data elements
NodeSet<std::string> topDataElems = filterChildElements(_xmlNSPrefix + "data", filterChildElements(_xmlNSPrefix + "datamodel", _scxml));
for (unsigned int i = 0; i < topDataElems.size(); i++) {
- initializeData(topDataElems[i]);
+ if (topDataElems[i].getNodeType() == Node_base::ELEMENT_NODE)
+ initializeData(Element<std::string>(topDataElems[i]));
}
}
@@ -843,7 +845,8 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>&
if(dataModelElems.size() > 0 && _dataModel) {
Arabica::XPath::NodeSet<std::string> dataElems = filterChildElements(_xmlNSPrefix + "data", dataModelElems[0]);
for (int j = 0; j < dataElems.size(); j++) {
- initializeData(dataElems[j]);
+ if (dataElems[j].getNodeType() == Node_base::ELEMENT_NODE)
+ initializeData(Element<std::string>(dataElems[j]));
}
}
stateElem.setAttribute("isFirstEntry", "");
diff --git a/src/uscxml/interpreter/InterpreterDraft7.cpp b/src/uscxml/interpreter/InterpreterDraft7.cpp
index 5f07cfc..e02a343 100644
--- a/src/uscxml/interpreter/InterpreterDraft7.cpp
+++ b/src/uscxml/interpreter/InterpreterDraft7.cpp
@@ -45,11 +45,11 @@ void InterpreterDraft7::interpret() {
}
if (_dataModel) {
- _dataModel.assign("_x.args", _cmdLineOptions);
+ _dataModel.assign("_x.args", _cmdLineOptions, Element<std::string>());
if (_httpServlet) {
Data data;
data.compound["location"] = Data(_httpServlet->getURL(), Data::VERBATIM);
- _dataModel.assign("_ioprocessors['http']", data);
+ _dataModel.assign("_ioprocessors['http']", data, Element<std::string>());
}
}
@@ -64,13 +64,15 @@ void InterpreterDraft7::interpret() {
// initialize all data elements
NodeSet<std::string> dataElems = _xpath.evaluate("//" + _xpathPrefix + "data", _document).asNodeSet();
for (unsigned int i = 0; i < dataElems.size(); i++) {
- initializeData(dataElems[i]);
+ if (dataElems[i].getNodeType() == Node_base::ELEMENT_NODE)
+ initializeData(Element<std::string>(dataElems[i]));
}
} else if(_dataModel) {
// initialize current data elements
NodeSet<std::string> topDataElems = filterChildElements(_xmlNSPrefix + "data", filterChildElements(_xmlNSPrefix + "datamodel", _scxml));
for (unsigned int i = 0; i < topDataElems.size(); i++) {
- initializeData(topDataElems[i]);
+ if (topDataElems[i].getNodeType() == Node_base::ELEMENT_NODE)
+ initializeData(Element<std::string>(topDataElems[i]));
}
}
@@ -117,7 +119,7 @@ void InterpreterDraft7::interpret() {
/**
* Called with a single data element from the topmost datamodel element.
*/
-void InterpreterDraft7::initializeData(const Arabica::DOM::Node<std::string>& data) {
+void InterpreterDraft7::initializeData(const Arabica::DOM::Element<std::string>& data) {
if (!_dataModel) {
LOG(ERROR) << "Cannot initialize data when no datamodel is given!";
return;
@@ -130,7 +132,7 @@ void InterpreterDraft7::initializeData(const Arabica::DOM::Node<std::string>& da
if (HAS_ATTR(data, "expr")) {
std::string value = ATTR(data, "expr");
- _dataModel.assign(ATTR(data, "id"), value);
+ _dataModel.assign(ATTR(data, "id"), value, data);
} else if (HAS_ATTR(data, "src")) {
URL srcURL(ATTR(data, "src"));
if (!srcURL.isAbsolute())
@@ -143,7 +145,7 @@ void InterpreterDraft7::initializeData(const Arabica::DOM::Node<std::string>& da
ss << srcURL;
_cachedURLs[srcURL.asString()] = srcURL;
}
- _dataModel.assign(ATTR(data, "id"), ss.str());
+ _dataModel.assign(ATTR(data, "id"), ss.str(), data);
} else if (data.hasChildNodes()) {
// search for the text node with the actual script
@@ -151,7 +153,7 @@ void InterpreterDraft7::initializeData(const Arabica::DOM::Node<std::string>& da
for (int i = 0; i < dataChilds.getLength(); i++) {
if (dataChilds.item(i).getNodeType() == Node_base::TEXT_NODE) {
Data value = Data(dataChilds.item(i).getNodeValue());
- _dataModel.assign(ATTR(data, "id"), value);
+ _dataModel.assign(ATTR(data, "id"), value, data);
break;
}
}
diff --git a/src/uscxml/interpreter/InterpreterDraft7.h b/src/uscxml/interpreter/InterpreterDraft7.h
index 7600041..6763e95 100644
--- a/src/uscxml/interpreter/InterpreterDraft7.h
+++ b/src/uscxml/interpreter/InterpreterDraft7.h
@@ -37,7 +37,7 @@ class InterpreterDraft7 : public InterpreterImpl {
const Arabica::XPath::NodeSet<std::string>& statesToEnter,
const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry);
- void initializeData(const Arabica::DOM::Node<std::string>& data);
+ void initializeData(const Arabica::DOM::Element<std::string>& data);
};
diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp
index 0146a31..2491fc4 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp
+++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp
@@ -392,7 +392,9 @@ double V8DataModel::evalAsNumber(const std::string& expr) {
return 0;
}
-void V8DataModel::assign(const std::string& location, const Arabica::DOM::Document<std::string>& doc) {
+void V8DataModel::assign(const std::string& location,
+ const Arabica::DOM::Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& dataElem) {
v8::Locker locker;
v8::HandleScope handleScope;
v8::Context::Scope contextScope(_contexts.front());
@@ -402,17 +404,21 @@ void V8DataModel::assign(const std::string& location, const Arabica::DOM::Docume
}
-void V8DataModel::assign(const std::string& location, const Data& data) {
+void V8DataModel::assign(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& dataElem) {
v8::Locker locker;
v8::HandleScope handleScope;
v8::Context::Scope contextScope(_contexts.front());
std::stringstream ssJSON;
ssJSON << data;
- assign(location, ssJSON.str());
+ assign(location, ssJSON.str(), dataElem);
}
-void V8DataModel::assign(const std::string& location, const std::string& expr) {
+void V8DataModel::assign(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& dataElem) {
v8::Locker locker;
v8::HandleScope handleScope;
v8::Context::Scope contextScope(_contexts.back());
diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h
index 760b638..7bea50c 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h
+++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h
@@ -39,10 +39,34 @@ public:
virtual void pushContext();
virtual void popContext();
+ virtual bool supportsJSON() { return true; }
+
virtual void eval(const std::string& expr);
- virtual void assign(const std::string& location, const Arabica::DOM::Document<std::string>& doc);
- virtual void assign(const std::string& location, const std::string& expr);
- virtual void assign(const std::string& location, const Data& data);
+ virtual void assign(const std::string& location,
+ const Arabica::DOM::Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& assignElem);
+ virtual void assign(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& assignElem);
+ virtual void assign(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& assignElem);
+
+ virtual void init(const std::string& location,
+ const Arabica::DOM::Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& dataElem) {
+ assign(location, doc, dataElem);
+ };
+ virtual void init(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& dataElem) {
+ assign(location, expr, dataElem);
+ }
+ virtual void init(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& dataElem) {
+ assign(location, data, dataElem);
+ }
virtual Data getStringAsData(const std::string& content);
virtual Data getValueAsData(const v8::Handle<v8::Value>& value);
diff --git a/src/uscxml/plugins/datamodel/null/NULLDataModel.cpp b/src/uscxml/plugins/datamodel/null/NULLDataModel.cpp
new file mode 100644
index 0000000..69970dd
--- /dev/null
+++ b/src/uscxml/plugins/datamodel/null/NULLDataModel.cpp
@@ -0,0 +1,121 @@
+#include "uscxml/Common.h"
+#include "NULLDataModel.h"
+
+#include "uscxml/Message.h"
+#include <glog/logging.h>
+
+#ifdef BUILD_AS_PLUGINS
+#include <Pluma/Connector.hpp>
+#endif
+
+namespace uscxml {
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_CONNECTOR
+bool connect(pluma::Host& host) {
+ host.add( new NULLDataModelProvider() );
+ return true;
+}
+#endif
+
+NULLDataModel::NULLDataModel() {
+}
+
+boost::shared_ptr<DataModelImpl> NULLDataModel::create(InterpreterImpl* interpreter) {
+ boost::shared_ptr<NULLDataModel> dm = boost::shared_ptr<NULLDataModel>(new NULLDataModel());
+ dm->_interpreter = interpreter;
+ return dm;
+}
+
+NULLDataModel::~NULLDataModel() {
+}
+
+void NULLDataModel::pushContext() {
+}
+
+void NULLDataModel::popContext() {
+}
+
+void NULLDataModel::initialize() {
+}
+
+void NULLDataModel::setEvent(const Event& event) {
+}
+
+Data NULLDataModel::getStringAsData(const std::string& content) {
+ Data data;
+ return data;
+}
+
+bool NULLDataModel::validate(const std::string& location, const std::string& schema) {
+ return true;
+}
+
+uint32_t NULLDataModel::getLength(const std::string& expr) {
+ return 0;
+}
+
+void NULLDataModel::eval(const std::string& expr) {
+}
+
+bool NULLDataModel::isDeclared(const std::string& expr) {
+ return true;
+}
+
+/**
+ * The boolean expression language consists of the In predicate only. It has the
+ * form 'In(id)', where id is the id of a state in the enclosing state machine.
+ * The predicate must return 'true' if and only if that state is in the current
+ * state configuration.
+ */
+bool NULLDataModel::evalAsBool(const std::string& expr) {
+ std::string trimmedExpr = expr;
+ boost::trim(trimmedExpr);
+ if (!boost::istarts_with(trimmedExpr, "in"))
+ return false;
+
+ // find string in between brackets
+ size_t start = trimmedExpr.find_first_of("(");
+ size_t end = trimmedExpr.find_last_of(")");
+ if (start == std::string::npos || end == std::string::npos || start >= end)
+ return false;
+ start++;
+
+ // split at comma
+ std::stringstream ss(trimmedExpr.substr(start, end - start));
+ std::vector<std::string> stateExprs;
+ std::string item;
+ while(std::getline(ss, item, ',')) {
+ stateExprs.push_back(item);
+ }
+
+ for (unsigned int i = 0; i < stateExprs.size(); i++) {
+ // remove ticks
+ size_t start = stateExprs[i].find_first_of("'");
+ size_t end = stateExprs[i].find_last_of("'");
+
+ std::string stateName;
+ if (start != std::string::npos && end != std::string::npos && start < end) {
+ start++;
+ stateName = stateExprs[i].substr(start, end - start);
+ } else {
+ stateName = stateExprs[i];
+ }
+
+ if (Interpreter::isMember(_interpreter->getState(stateName), _interpreter->getConfiguration())) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+}
+
+std::string NULLDataModel::evalAsString(const std::string& expr) {
+ return expr;
+}
+
+double NULLDataModel::evalAsNumber(const std::string& expr) {
+ return 0;
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/plugins/datamodel/null/NULLDataModel.h b/src/uscxml/plugins/datamodel/null/NULLDataModel.h
new file mode 100644
index 0000000..eaa9dbd
--- /dev/null
+++ b/src/uscxml/plugins/datamodel/null/NULLDataModel.h
@@ -0,0 +1,77 @@
+#ifndef NULLDATAMODEL_H_KN8TWG0V
+#define NULLDATAMODEL_H_KN8TWG0V
+
+#include "uscxml/Interpreter.h"
+#include <list>
+
+#ifdef BUILD_AS_PLUGINS
+#include "uscxml/plugins/Plugins.h"
+#endif
+
+namespace uscxml {
+class Event;
+class Data;
+}
+
+namespace uscxml {
+
+class NULLDataModel : public DataModelImpl {
+public:
+ NULLDataModel();
+ virtual ~NULLDataModel();
+ virtual boost::shared_ptr<DataModelImpl> create(InterpreterImpl* interpreter);
+
+ virtual std::set<std::string> getNames() {
+ std::set<std::string> names;
+ names.insert("null");
+ return names;
+ }
+
+ virtual void initialize();
+ virtual void setEvent(const Event& event);
+
+ virtual bool validate(const std::string& location, const std::string& schema);
+
+ virtual uint32_t getLength(const std::string& expr);
+ virtual void pushContext();
+ virtual void popContext();
+
+ virtual void eval(const std::string& expr);
+ virtual void assign(const std::string& location,
+ const Arabica::DOM::Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& assignElem) {}
+ virtual void assign(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& assignElem) {}
+ virtual void assign(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& assignElem) {}
+
+ virtual void init(const std::string& location,
+ const Arabica::DOM::Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& dataElem) {};
+ virtual void init(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& dataElem) {};
+ virtual void init(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& dataElem) {};
+
+ virtual Data getStringAsData(const std::string& content);
+ virtual bool isDeclared(const std::string& expr);
+
+ virtual std::string evalAsString(const std::string& expr);
+ virtual bool evalAsBool(const std::string& expr);
+ virtual double evalAsNumber(const std::string& expr);
+
+protected:
+
+};
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_INHERIT_PROVIDER(NULLDataModel, DataModelImpl);
+#endif
+
+}
+
+#endif /* end of include guard: NULLDATAMODEL_H_KN8TWG0V */
diff --git a/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp
new file mode 100644
index 0000000..f874c86
--- /dev/null
+++ b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp
@@ -0,0 +1,470 @@
+#include "uscxml/Common.h"
+#include "XPathDataModel.h"
+
+#include "uscxml/Message.h"
+#include <glog/logging.h>
+
+#ifdef BUILD_AS_PLUGINS
+#include <Pluma/Connector.hpp>
+#endif
+
+namespace uscxml {
+
+using namespace Arabica::XPath;
+using namespace Arabica::DOM;
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_CONNECTOR
+bool connect(pluma::Host& host) {
+ host.add( new XPathDataModelProvider() );
+ return true;
+}
+#endif
+
+XPathDataModel::XPathDataModel() {
+}
+
+boost::shared_ptr<DataModelImpl> XPathDataModel::create(InterpreterImpl* interpreter) {
+ boost::shared_ptr<XPathDataModel> dm = boost::shared_ptr<XPathDataModel>(new XPathDataModel());
+ dm->_interpreter = interpreter;
+// dm->_xpath.setVariableResolver(_varResolver);
+// dm->_xpath->setVariableCompileTimeResolver(_varCTResolver);
+// dm->_xpath->setNamespaceContext(interpreter->getNSContext());
+
+ dm->_funcResolver.setInterpreter(interpreter);
+ dm->_xpath.setFunctionResolver(dm->_funcResolver);
+ dm->_xpath.setVariableResolver(dm->_varResolver);
+
+ dm->_domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation();
+ dm->_doc = dm->_domFactory.createDocument("http://www.w3.org/2005/07/scxml", "", 0);
+ dm->_datamodel = dm->_doc.createElement("datamodel");
+ dm->_doc.appendChild(dm->_datamodel);
+
+ Element<std::string> ioProcElem = dm->_doc.createElement("data");
+ ioProcElem.setAttribute("id", "_ioprocessors");
+ std::map<std::string, IOProcessor>::const_iterator ioProcIter = interpreter->getIOProcessors().begin();
+ while(ioProcIter != interpreter->getIOProcessors().end()) {
+ Element<std::string> ioProc = dm->_doc.createElement("processor");
+ ioProc.setAttribute("name", ioProcIter->first);
+
+ Data ioProcData = ioProcIter->second.getDataModelVariables();
+ Element<std::string> ioProcLoc = dm->_doc.createElement("location");
+ Text<std::string> ioProcLocText = dm->_doc.createTextNode(ioProcData.compound["location"].atom);
+ ioProcLoc.appendChild(ioProcLocText);
+ ioProc.appendChild(ioProcLoc);
+ ioProcElem.appendChild(ioProc);
+
+ ioProcIter++;
+ }
+ dm->_datamodel.appendChild(ioProcElem);
+
+ NodeSet<std::string> ioProcNodeSet;
+ ioProcNodeSet.push_back(ioProcElem);
+ dm->_varResolver.setVariable("_ioprocessors", ioProcNodeSet);
+
+
+ Element<std::string> sessIdElem = dm->_doc.createElement("data");
+ sessIdElem.setAttribute("id", "_sessionid");
+ Text<std::string> sessIdText = dm->_doc.createTextNode(interpreter->getSessionId());
+ sessIdElem.appendChild(sessIdText);
+ dm->_datamodel.appendChild(sessIdElem);
+
+ NodeSet<std::string> sessIdNodeSet;
+ sessIdNodeSet.push_back(sessIdElem);
+ dm->_varResolver.setVariable("_sessionid", sessIdNodeSet);
+
+
+ Element<std::string> nameElem = dm->_doc.createElement("data");
+ nameElem.setAttribute("id", "_name");
+ Text<std::string> nameText = dm->_doc.createTextNode(interpreter->getName());
+ nameElem.appendChild(nameText);
+ dm->_datamodel.appendChild(nameElem);
+
+ NodeSet<std::string> nameNodeSet;
+ nameNodeSet.push_back(nameElem);
+ dm->_varResolver.setVariable("_name", nameNodeSet);
+
+ return dm;
+}
+
+XPathDataModel::~XPathDataModel() {
+}
+
+void XPathDataModel::pushContext() {
+}
+
+void XPathDataModel::popContext() {
+}
+
+void XPathDataModel::initialize() {
+}
+
+void XPathDataModel::setEvent(const Event& event) {
+ Element<std::string> eventElem = _doc.createElement("data");
+ eventElem.setAttribute("id", "_event");
+
+ Element<std::string> eventDataElem = _doc.createElement("data");
+
+ NodeSet<std::string> eventNodeSet;
+ if (event.params.size() > 0) {
+ std::multimap<std::string, std::string>::const_iterator paramIter = event.params.begin();
+ while(paramIter != event.params.end()) {
+ Element<std::string> eventParamElem = _doc.createElement("data");
+ Text<std::string> eventParamText = _doc.createTextNode(paramIter->second);
+
+ eventParamElem.setAttribute("id", paramIter->first);
+ eventParamElem.appendChild(eventParamText);
+ eventDataElem.appendChild(eventParamElem);
+ paramIter++;
+ }
+ }
+ if (event.namelist.size() > 0) {
+ std::map<std::string, std::string>::const_iterator namelistIter = event.namelist.begin();
+ while(namelistIter != event.namelist.end()) {
+ Element<std::string> eventNamelistElem = _doc.createElement("data");
+ Text<std::string> eventNamelistText = _doc.createTextNode(namelistIter->second);
+
+ eventNamelistElem.setAttribute("id", namelistIter->first);
+ eventNamelistElem.appendChild(eventNamelistText);
+ eventDataElem.appendChild(eventNamelistElem);
+ namelistIter++;
+ }
+ }
+ if (event.content.size() > 0) {
+ eventDataElem.setNodeValue(event.content);
+ }
+
+ eventElem.appendChild(eventDataElem);
+ eventNodeSet.push_back(eventElem);
+
+// std::cout << eventElem << std::endl;
+
+ // do we need to replace an existing event?
+ Node<std::string> oldEventElem = _datamodel.getFirstChild();
+ while(oldEventElem) {
+ if (oldEventElem.getNodeType() == Node_base::ELEMENT_NODE) {
+ if (HAS_ATTR(oldEventElem, "id") && boost::iequals(ATTR(oldEventElem, "id"), "_event"))
+ break;
+ }
+ oldEventElem = oldEventElem.getNextSibling();
+ }
+
+ if (oldEventElem) {
+ _datamodel.replaceChild(eventElem, oldEventElem);
+ } else {
+ _datamodel.appendChild(eventElem);
+ }
+ _varResolver.setVariable("_event", eventNodeSet);
+}
+
+Data XPathDataModel::getStringAsData(const std::string& content) {
+ Data data;
+ return data;
+}
+
+bool XPathDataModel::validate(const std::string& location, const std::string& schema) {
+ return true;
+}
+
+uint32_t XPathDataModel::getLength(const std::string& expr) {
+ return 0;
+}
+
+void XPathDataModel::eval(const std::string& expr) {
+ XPathValue<std::string> result = _xpath.evaluate_expr(expr, _doc);
+}
+
+bool XPathDataModel::isDeclared(const std::string& expr) {
+ return true;
+}
+
+bool XPathDataModel::evalAsBool(const std::string& expr) {
+ XPathValue<std::string> result = _xpath.evaluate_expr(expr, _doc);
+ return result.asBool();
+}
+
+std::string XPathDataModel::evalAsString(const std::string& expr) {
+ XPathValue<std::string> result = _xpath.evaluate_expr(expr, _doc);
+ return result.asString();
+}
+
+double XPathDataModel::evalAsNumber(const std::string& expr) {
+ XPathValue<std::string> result = _xpath.evaluate_expr(expr, _doc);
+ return result.asNumber();
+}
+
+void XPathDataModel::assign(const std::string& location,
+ const Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& dataElem) {
+ XPathValue<std::string> key = _xpath.evaluate_expr(location, _doc);
+ NodeSet<std::string> nodeSet;
+ nodeSet.push_back(doc.getDocumentElement());
+ assign(key, nodeSet, dataElem);
+}
+
+void XPathDataModel::assign(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& dataElem) {
+// assert(false);
+// std::cout << location << " = " << data << std::endl;
+}
+
+void XPathDataModel::assign(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& dataElem) {
+ std::string realExpr = (HAS_ATTR(dataElem, "expr") ? ATTR(dataElem, "expr") : expr);
+ XPathValue<std::string> key = _xpath.evaluate_expr(location, _doc);
+ XPathValue<std::string> value = _xpath.evaluate_expr(realExpr, _doc);
+ assign(key, value, dataElem);
+}
+
+void XPathDataModel::init(const std::string& location,
+ const Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& dataElem) {
+ Element<std::string> container = _doc.createElement("data");
+ container.setAttribute("id", location);
+ Element<std::string> data = doc.getDocumentElement();
+ if (data.hasChildNodes()) {
+ Node<std::string> dataClone = _doc.importNode(data, true);
+ container.appendChild(dataClone);
+ }
+ _datamodel.appendChild(container);
+
+ // put data element into nodeset and bind to xpath variable
+ NodeSet<std::string> nodeSet;
+ nodeSet.push_back(container);
+ _varResolver.setVariable(location, nodeSet);
+}
+
+void XPathDataModel::init(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& dataElem) {
+ Element<std::string> data = _doc.createElement("data");
+ data.setAttribute("id", location);
+
+ if (expr.length() > 0) {
+ Text<std::string> textNode = _doc.createTextNode(expr.c_str());
+ data.appendChild(textNode);
+ _datamodel.appendChild(data);
+ }
+
+ // put data element into nodeset and bind to xpath variable
+ NodeSet<std::string> nodeSet;
+ nodeSet.push_back(data);
+ _varResolver.setVariable(location, nodeSet);
+}
+
+void XPathDataModel::init(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& dataElem) {
+ assert(false);
+}
+
+void XPathDataModel::assign(XPathValue<std::string>& key,
+ const XPathValue<std::string>& value,
+ const Arabica::DOM::Element<std::string>& assignElem) {
+ switch (value.type()) {
+ case STRING:
+ assign(key, value.asString(), assignElem);
+ break;
+ case BOOL:
+ assign(key, value.asBool(), assignElem);
+ break;
+ case NUMBER:
+ assign(key, value.asNumber(), assignElem);
+ break;
+ case NODE_SET:
+ assign(key, value.asNodeSet(), assignElem);
+ break;
+ case ANY:
+ throw Event("error.execution", Event::PLATFORM);
+ }
+}
+
+void XPathDataModel::assign(XPathValue<std::string>& key,
+ const std::string& value,
+ const Arabica::DOM::Element<std::string>& assignElem) {
+ switch (key.type()) {
+ case NODE_SET: {
+ if (key.asNodeSet().size() == 0)
+ return;
+ Node<std::string> node = key.asNodeSet()[0];
+ switch (node.getNodeType()) {
+ case Node_base::ATTRIBUTE_NODE: {
+ Attr<std::string> attr(node);
+ attr.setValue(value);
+ break;
+ }
+ case Node_base::TEXT_NODE: {
+ Text<std::string> text(node);
+ text.setNodeValue(value);
+ break;
+ }
+ case Node_base::ELEMENT_NODE: {
+ Element<std::string> element(node);
+ if (HAS_ATTR(assignElem, "type") && boost::iequals(ATTR(assignElem, "type"), "addattribute")) {
+ // addattribute: Add an attribute with the name specified by 'attr'
+ // and value specified by 'expr' to the node specified by 'location'.
+ if (!HAS_ATTR(assignElem, "attr"))
+ throw Event("error.execution", Event::PLATFORM);
+ element.setAttribute(ATTR(assignElem, "attr"), value);
+ }
+ break;
+ }
+ default:
+ throw Event("error.execution", Event::PLATFORM);
+ break;
+ }
+ break;
+ }
+ case STRING:
+ case BOOL:
+ case NUMBER:
+ case ANY:
+ throw Event("error.execution", Event::PLATFORM);
+ break;
+ default:
+ break;
+ }
+}
+
+void XPathDataModel::assign(XPathValue<std::string>& key,
+ const double value,
+ const Arabica::DOM::Element<std::string>& assignElem) {
+}
+
+void XPathDataModel::assign(XPathValue<std::string>& key,
+ const bool value,
+ const Arabica::DOM::Element<std::string>& assignElem) {
+}
+
+void XPathDataModel::assign(XPathValue<std::string>& key,
+ const NodeSet<std::string>& value,
+ const Arabica::DOM::Element<std::string>& assignElem) {
+ switch (key.type()) {
+ case NODE_SET: {
+ if (key.asNodeSet().size() == 0)
+ return;
+ Node<std::string> node = key.asNodeSet()[0];
+ switch (node.getNodeType()) {
+ case Node_base::ELEMENT_NODE: {
+ Element<std::string> element(node);
+ if (false) {
+ } else if (HAS_ATTR(assignElem, "type") && boost::iequals(ATTR(assignElem, "type"), "firstchild")) {
+ // firstchild: Insert the value specified by 'expr' before all of the children at 'location'.
+ for (int i = value.size(); i; i--) {
+ Node<std::string> importedNode = _doc.importNode(value[i-1], true);
+ element.insertBefore(importedNode, element.getFirstChild());
+ }
+ } else if (HAS_ATTR(assignElem, "type") && boost::iequals(ATTR(assignElem, "type"), "lastchild")) {
+ // lastchild: Insert the value specified by 'expr' after all of the children at 'location'.
+ for (int i = 0; i < value.size(); i++) {
+ Node<std::string> importedNode = _doc.importNode(value[i], true);
+ element.appendChild(importedNode);
+ }
+ } else if (HAS_ATTR(assignElem, "type") && boost::iequals(ATTR(assignElem, "type"), "previoussibling")) {
+ // previoussibling: Insert the value specified by 'expr' before the
+ // node specified by 'location', keeping the same parent.
+ Node<std::string> parent = element.getParentNode();
+ if (!parent)
+ throw Event("error.execution", Event::PLATFORM);
+ for (int i = 0; i < value.size(); i++) {
+ Node<std::string> importedNode = _doc.importNode(value[i], true);
+ parent.insertBefore(importedNode, element);
+ }
+ } else if (HAS_ATTR(assignElem, "type") && boost::iequals(ATTR(assignElem, "type"), "nextsibling")) {
+ // nextsibling: Insert the value specified by 'expr' after the node
+ // specified by 'location', keeping the same parent.
+ Node<std::string> parent = element.getParentNode();
+ if (!parent)
+ throw Event("error.execution", Event::PLATFORM);
+ for (int i = value.size(); i; i--) {
+ Node<std::string> importedNode = _doc.importNode(value[i-1], true);
+ Node<std::string> nextSibling = element.getNextSibling();
+ if (nextSibling) {
+ parent.insertBefore(importedNode, element.getNextSibling());
+ } else {
+ parent.appendChild(importedNode);
+ }
+ }
+ } else if (HAS_ATTR(assignElem, "type") && boost::iequals(ATTR(assignElem, "type"), "replace")) {
+ // replace: Replace the node specified by 'location' by the value specified by 'expr'.
+ Node<std::string> parent = element.getParentNode();
+ if (!parent)
+ throw Event("error.execution", Event::PLATFORM);
+ if (value.size() != 1)
+ throw Event("error.execution", Event::PLATFORM);
+ Node<std::string> importedNode = _doc.importNode(value[0], true);
+ parent.replaceChild(importedNode, element);
+ } else if (HAS_ATTR(assignElem, "type") && boost::iequals(ATTR(assignElem, "type"), "delete")) {
+ // delete: Delete the node specified by 'location'. ('expr' is ignored.).
+ Node<std::string> parent = element.getParentNode();
+ if (!parent)
+ throw Event("error.execution", Event::PLATFORM);
+ parent.removeChild(element);
+ } else {
+ // replacechildren: Replace all the children at 'location' with the value specified by 'expr'.
+ while(element.hasChildNodes())
+ element.removeChild(element.getChildNodes().item(0));
+ for (int i = 0; i < value.size(); i++) {
+ Node<std::string> importedNode = _doc.importNode(value[i], true);
+ element.appendChild(importedNode);
+ }
+ }
+ break;
+ }
+ default:
+ throw Event("error.execution", Event::PLATFORM);
+ break;
+ }
+ break;
+ }
+ case STRING:
+ case BOOL:
+ case NUMBER:
+ case ANY:
+ throw Event("error.execution", Event::PLATFORM);
+ break;
+ }
+}
+
+XPathValue<std::string>
+ NodeSetVariableResolver::resolveVariable(const std::string& namepaceUri,
+ const std::string& name) const {
+ std::map<std::string, NodeSet<std::string> >::const_iterator n = _variables.find(name);
+ if(n == _variables.end()) {
+ throw Event("error.execution");
+ }
+ return XPathValue<std::string>(new NodeSetValue<std::string>(n->second));
+}
+
+XPathFunction<std::string>*
+ XPathFunctionResolver::resolveFunction(const std::string& namespace_uri,
+ const std::string& name,
+ const std::vector<XPathExpression<std::string> >& argExprs) const {
+ if (boost::iequals(name, "in")) {
+ return new XPathFunctionIn(1, -1, argExprs, _interpreter);
+ }
+ return _xpathFuncRes.resolveFunction(namespace_uri, name, argExprs);
+}
+
+std::vector<std::pair<std::string, std::string> > XPathFunctionResolver::validNames() const {
+ std::vector<std::pair<std::string, std::string> > names = _xpathFuncRes.validNames();
+ names.push_back(std::make_pair("", "In"));
+ return names;
+}
+
+bool XPathFunctionIn::doEvaluate(const Node<std::string>& context,
+ const ExecutionContext<std::string>& executionContext) const {
+ for (int i = 0; i < argCount(); i++) {
+ XPathValue<std::string> stateName = arg(i, context, executionContext);
+ if (stateName.type() == STRING) {
+ if (!Interpreter::isMember(_interpreter->getState(stateName.asString()), _interpreter->getConfiguration())) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/plugins/datamodel/xpath/XPathDataModel.h b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.h
new file mode 100644
index 0000000..d028129
--- /dev/null
+++ b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.h
@@ -0,0 +1,143 @@
+#ifndef XPATHDATAMODEL_H_KN8TWG0V
+#define XPATHDATAMODEL_H_KN8TWG0V
+
+#include "uscxml/Interpreter.h"
+#include <list>
+
+#ifdef BUILD_AS_PLUGINS
+#include "uscxml/plugins/Plugins.h"
+#endif
+
+namespace uscxml {
+class Event;
+class Data;
+}
+
+namespace uscxml {
+
+class XPathFunctionIn : public Arabica::XPath::BooleanXPathFunction<std::string> {
+public:
+ XPathFunctionIn(int minArgs,
+ int maxArgs,
+ const std::vector<Arabica::XPath::XPathExpression<std::string> >& args,
+ InterpreterImpl* interpreter) :
+ BooleanXPathFunction(minArgs, maxArgs, args),
+ _interpreter(interpreter) {}
+
+protected:
+ bool doEvaluate(const Arabica::DOM::Node<std::string>& context,
+ const Arabica::XPath::ExecutionContext<std::string>& executionContext) const;
+ InterpreterImpl* _interpreter;
+};
+
+class XPathFunctionResolver : public Arabica::XPath::FunctionResolver<std::string> {
+public:
+ virtual ~XPathFunctionResolver() { }
+
+ virtual Arabica::XPath::XPathFunction<std::string>*
+ resolveFunction(const std::string& namespace_uri,
+ const std::string& name,
+ const std::vector<Arabica::XPath::XPathExpression<std::string> >& argExprs) const;
+
+ virtual std::vector<std::pair<std::string, std::string> > validNames() const;
+ void setInterpreter(InterpreterImpl* interpreter) { _interpreter = interpreter; }
+protected:
+ Arabica::XPath::StandardXPathFunctionResolver<std::string> _xpathFuncRes;
+ InterpreterImpl* _interpreter;
+};
+
+class NodeSetVariableResolver : public Arabica::XPath::VariableResolver<std::string> {
+public:
+ Arabica::XPath::XPathValue<std::string> resolveVariable(const std::string& namepaceUri,
+ const std::string& name) const;
+ void setVariable(const std::string& name, const Arabica::XPath::NodeSet<std::string>& value) {
+ _variables[name] = value;
+ }
+
+private:
+ std::map<std::string, Arabica::XPath::NodeSet<std::string> > _variables;
+};
+
+class XPathDataModel : public DataModelImpl {
+public:
+ XPathDataModel();
+ virtual ~XPathDataModel();
+ virtual boost::shared_ptr<DataModelImpl> create(InterpreterImpl* interpreter);
+
+ virtual std::set<std::string> getNames() {
+ std::set<std::string> names;
+ names.insert("xpath");
+ return names;
+ }
+
+ virtual void initialize();
+ virtual void setEvent(const Event& event);
+
+ virtual bool validate(const std::string& location, const std::string& schema);
+
+ virtual uint32_t getLength(const std::string& expr);
+ virtual void pushContext();
+ virtual void popContext();
+
+ virtual void eval(const std::string& expr);
+ virtual void assign(const std::string& location,
+ const Arabica::DOM::Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& assignElem);
+ virtual void assign(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& assignElem);
+ virtual void assign(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& assignElem);
+
+ virtual void init(const std::string& location,
+ const Arabica::DOM::Document<std::string>& doc,
+ const Arabica::DOM::Element<std::string>& dataElem);
+ virtual void init(const std::string& location,
+ const std::string& expr,
+ const Arabica::DOM::Element<std::string>& dataElem);
+ virtual void init(const std::string& location,
+ const Data& data,
+ const Arabica::DOM::Element<std::string>& dataElem);
+
+ virtual Data getStringAsData(const std::string& content);
+ virtual bool isDeclared(const std::string& expr);
+
+ virtual std::string evalAsString(const std::string& expr);
+ virtual bool evalAsBool(const std::string& expr);
+ virtual double evalAsNumber(const std::string& expr);
+
+protected:
+ Arabica::XPath::XPath<std::string> _xpath;
+ Arabica::DOM::DOMImplementation<std::string> _domFactory;
+ Arabica::DOM::Element<std::string> _datamodel;
+ Arabica::DOM::Document<std::string> _doc;
+
+ void assign(Arabica::XPath::XPathValue<std::string>& key,
+ const Arabica::XPath::XPathValue<std::string>& value,
+ const Arabica::DOM::Element<std::string>& assignElem);
+ void assign(Arabica::XPath::XPathValue<std::string>& key,
+ const std::string& value,
+ const Arabica::DOM::Element<std::string>& assignElem);
+ void assign(Arabica::XPath::XPathValue<std::string>& key,
+ const double value,
+ const Arabica::DOM::Element<std::string>& assignElem);
+ void assign(Arabica::XPath::XPathValue<std::string>& key,
+ const bool value,
+ const Arabica::DOM::Element<std::string>& assignElem);
+ void assign(Arabica::XPath::XPathValue<std::string>& key,
+ const Arabica::XPath::NodeSet<std::string>& value,
+ const Arabica::DOM::Element<std::string>& assignElem);
+
+ NodeSetVariableResolver _varResolver;
+ XPathFunctionResolver _funcResolver;
+
+};
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_INHERIT_PROVIDER(XPathDataModel, DataModelImpl);
+#endif
+
+}
+
+#endif /* end of include guard: XPATHDATAMODEL_H_KN8TWG0V */
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index d7c305e..a9ec9f1 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -45,6 +45,12 @@ if (NOT WIN32)
target_link_libraries(test-arabica-events uscxml)
add_test(test-arabica-events ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-arabica-events ${CMAKE_SOURCE_DIR}/test/samples/uscxml/arabica/test-arabica-events.xml)
set_target_properties(test-arabica-events PROPERTIES FOLDER "Tests")
+
+ add_executable(test-arabica-xpath src/test-arabica-xpath.cpp)
+ target_link_libraries(test-arabica-xpath uscxml)
+ add_test(test-arabica-xpath ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-arabica-xpath)
+ set_target_properties(test-arabica-xpath PROPERTIES FOLDER "Tests")
+
endif()
add_executable(test-url src/test-url.cpp)
diff --git a/test/samples/w3c/ecma/test463.scxml b/test/samples/w3c/ecma/test463.scxml
index 4f32b84..8ff0555 100644
--- a/test/samples/w3c/ecma/test463.scxml
+++ b/test/samples/w3c/ecma/test463.scxml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?><!-- test that the <data> tag creates an element in the XML datamodel with the correct name and id attr
and binds an XPath variable to it --><scxml xmlns="http://www.w3.org/2005/07/scxml" xmlns:conf="http://www.w3.org/2005/scxml-conformance" initial="s0" version="1.0" datamodel="xpath">
<datamodel>
- <data id="foo" expr="2"/>
+ <data id="foo" expr="20"/>
</datamodel>
<state id="s0">
diff --git a/test/src/test-arabica-xpath.cpp b/test/src/test-arabica-xpath.cpp
new file mode 100644
index 0000000..2bcf605
--- /dev/null
+++ b/test/src/test-arabica-xpath.cpp
@@ -0,0 +1,198 @@
+#include <XPath/XPath.hpp>
+#include <DOM/Simple/DOMImplementation.hpp>
+#include <DOM/io/Stream.hpp>
+#include <iostream>
+#include <string>
+
+#define string_type std::string
+#define string_adaptor Arabica::default_string_adaptor<std::string>
+
+typedef string_adaptor SA;
+
+class NodeSetVariableResolver : public Arabica::XPath::VariableResolver<string_type, string_adaptor>
+{
+ //typedef string_adaptorstring_adaptor;
+public:
+ virtual Arabica::XPath::XPathValue<string_type, string_adaptor> resolveVariable(const string_type& /* namepace_uri */,
+ const string_type& name) const
+ {
+ using namespace Arabica::XPath;
+ typename VarMap::const_iterator n = map_.find(name);
+ if(n == map_.end())
+ throw UnboundVariableException(string_adaptor::asStdString(name));
+ return XPathValue<string_type, string_adaptor>(new NodeSetValue<string_type, string_adaptor>((*n).second));
+ } // resolveVariable
+
+ void setVariable(const string_type& name, const Arabica::XPath::NodeSet<string_type, string_adaptor>& value)
+ {
+ map_[name] = value;
+ } // setVariable
+
+private:
+ typedef std::map<string_type, Arabica::XPath::NodeSet<string_type, string_adaptor> > VarMap;
+ VarMap map_;
+}; // class NodeSetVariableResolver
+
+Arabica::XPath::XPath<string_type, string_adaptor> parser;
+Arabica::DOM::DOMImplementation<string_type, string_adaptor> factory_;
+Arabica::DOM::Document<string_type, string_adaptor> document_;
+
+Arabica::DOM::Element<string_type, string_adaptor> root_;
+Arabica::DOM::Element<string_type, string_adaptor> element1_;
+Arabica::DOM::Element<string_type, string_adaptor> element2_;
+Arabica::DOM::Element<string_type, string_adaptor> element3_;
+Arabica::DOM::Element<string_type, string_adaptor> spinkle_;
+
+Arabica::DOM::Attr<string_type, string_adaptor> attr_;
+Arabica::DOM::Text<string_type, string_adaptor> text_;
+Arabica::DOM::Comment<string_type, string_adaptor> comment_;
+Arabica::DOM::ProcessingInstruction<string_type, string_adaptor> processingInstruction_;
+Arabica::DOM::Document<string_type, string_adaptor> chapters_;
+Arabica::DOM::Document<string_type, string_adaptor> numbers_;
+
+class StringVariableResolver : public Arabica::XPath::VariableResolver<string_type, string_adaptor>
+{
+public:
+ virtual Arabica::XPath::XPathValue<string_type, string_adaptor> resolveVariable(const string_type& /* namespace_uri */,
+ const string_type& name) const
+ {
+ using namespace Arabica::XPath;
+ typename VarMap::const_iterator n = map_.find(name);
+ if(n == map_.end())
+ throw UnboundVariableException(string_adaptor::asStdString(name));
+ return XPathValue<string_type, string_adaptor>(new StringValue<string_type, string_adaptor>((*n).second));
+ } // resolveVariable
+
+ void setVariable(const string_type& name, const string_type& value)
+ {
+ map_[name] = value;
+ } // setVariable
+
+private:
+ typedef std::map<string_type, string_type> VarMap;
+ VarMap map_;
+}; // StringVariableResolver
+
+
+int main(int argc, char** argv) {
+
+ factory_ = Arabica::SimpleDOM::DOMImplementation<string_type, string_adaptor>::getDOMImplementation();
+ document_ = factory_.createDocument(SA::construct_from_utf8(""), SA::construct_from_utf8(""), 0);
+ root_ = document_.createElement("root");
+ document_.appendChild(root_);
+ assert(root_);
+
+ element1_ = document_.createElement(SA::construct_from_utf8("child1"));
+ element2_ = document_.createElement(SA::construct_from_utf8("child2"));
+ element3_ = document_.createElement(SA::construct_from_utf8("child3"));
+
+ element1_.setAttribute(SA::construct_from_utf8("one"), SA::construct_from_utf8("1"));
+
+ element2_.setAttribute(SA::construct_from_utf8("one"), SA::construct_from_utf8("1"));
+ element2_.setAttribute(SA::construct_from_utf8("two"), SA::construct_from_utf8("1"));
+ element2_.setAttribute(SA::construct_from_utf8("three"), SA::construct_from_utf8("1"));
+ element2_.setAttribute(SA::construct_from_utf8("four"), SA::construct_from_utf8("1"));
+
+ text_ = document_.createTextNode(SA::construct_from_utf8("data"));
+ comment_ = document_.createComment(SA::construct_from_utf8("comment"));
+ processingInstruction_ = document_.createProcessingInstruction(SA::construct_from_utf8("target"), SA::construct_from_utf8("data"));
+ element2_.appendChild(text_);
+ spinkle_ = document_.createElement(SA::construct_from_utf8("spinkle"));
+ element2_.appendChild(spinkle_);
+ element2_.appendChild(comment_);
+ element2_.appendChild(processingInstruction_);
+
+ attr_ = element1_.getAttributeNode(SA::construct_from_utf8("one"));
+
+ root_.appendChild(element1_);
+ root_.appendChild(element2_);
+ root_.appendChild(element3_);
+
+ chapters_ = factory_.createDocument(SA::construct_from_utf8(""), SA::construct_from_utf8(""), 0);
+ chapters_.appendChild(chapters_.createElement(SA::construct_from_utf8("document")));
+ chapters_.getFirstChild().appendChild(chapters_.createElement(SA::construct_from_utf8("chapter"))).appendChild(chapters_.createTextNode(SA::construct_from_utf8("one")));
+ chapters_.getFirstChild().appendChild(chapters_.createElement(SA::construct_from_utf8("chapter"))).appendChild(chapters_.createTextNode(SA::construct_from_utf8("two")));
+ chapters_.getFirstChild().appendChild(chapters_.createElement(SA::construct_from_utf8("chapter"))).appendChild(chapters_.createTextNode(SA::construct_from_utf8("three")));
+ chapters_.getFirstChild().appendChild(chapters_.createElement(SA::construct_from_utf8("chapter"))).appendChild(chapters_.createTextNode(SA::construct_from_utf8("four")));
+ chapters_.getFirstChild().appendChild(chapters_.createElement(SA::construct_from_utf8("chapter"))).appendChild(chapters_.createTextNode(SA::construct_from_utf8("five")));
+
+ numbers_ = factory_.createDocument(SA::construct_from_utf8(""), SA::construct_from_utf8(""), 0);
+ numbers_.appendChild(numbers_.createElement(SA::construct_from_utf8("doc")));
+ numbers_.getFirstChild().appendChild(numbers_.createElement(SA::construct_from_utf8("number"))).appendChild(numbers_.createTextNode(SA::construct_from_utf8("1")));
+ numbers_.getFirstChild().appendChild(numbers_.createElement(SA::construct_from_utf8("number"))).appendChild(numbers_.createTextNode(SA::construct_from_utf8("2")));
+ numbers_.getFirstChild().appendChild(numbers_.createElement(SA::construct_from_utf8("number"))).appendChild(numbers_.createTextNode(SA::construct_from_utf8("3")));
+ numbers_.getFirstChild().appendChild(numbers_.createElement(SA::construct_from_utf8("number"))).appendChild(numbers_.createTextNode(SA::construct_from_utf8("4")));
+ numbers_.getFirstChild().appendChild(numbers_.createElement(SA::construct_from_utf8("number"))).appendChild(numbers_.createTextNode(SA::construct_from_utf8("5")));
+ numbers_.getFirstChild().appendChild(numbers_.createElement(SA::construct_from_utf8("number"))).appendChild(numbers_.createTextNode(SA::construct_from_utf8("6")));
+ numbers_.getFirstChild().appendChild(numbers_.createElement(SA::construct_from_utf8("number"))).appendChild(numbers_.createTextNode(SA::construct_from_utf8("7")));
+ numbers_.getFirstChild().appendChild(numbers_.createElement(SA::construct_from_utf8("number"))).appendChild(numbers_.createTextNode(SA::construct_from_utf8("8")));
+ numbers_.getFirstChild().appendChild(numbers_.createElement(SA::construct_from_utf8("number"))).appendChild(numbers_.createTextNode(SA::construct_from_utf8("9")));
+ std::cout << document_ << std::endl;
+ std::cout << numbers_ << std::endl;
+ std::cout << chapters_ << std::endl;
+
+
+ if (false) {
+ using namespace Arabica::XPath;
+ StringVariableResolver svr;
+ svr.setVariable(SA::construct_from_utf8("index"), SA::construct_from_utf8("1"));
+
+ parser.setVariableResolver(svr);
+ XPathValue<string_type, string_adaptor> result = parser.evaluate(SA::construct_from_utf8("/root/*[@two = $index]"), document_);
+ assert(NODE_SET == result.type());
+ assert(element2_ == result.asNodeSet()[0]);
+
+ parser.resetVariableResolver();
+ } // test18
+
+ if (false) {
+ using namespace Arabica::XPath;
+ XPathExpression<string_type, string_adaptor> xpath = parser.compile(SA::construct_from_utf8("root/*[position() = 2]"));
+ XPathValue<string_type, string_adaptor> result = xpath.evaluate(document_);
+
+ assert(NODE_SET == result.type());
+ assert(1 == result.asNodeSet().size());
+ Arabica::DOM::Node<string_type, string_adaptor> n = result.asNodeSet()[0];
+ assert(element2_ == n);
+ } // test19
+
+ if (false) {
+ using namespace Arabica::XPath;
+ Arabica::DOM::DocumentFragment<string_type, string_adaptor> frag = document_.createDocumentFragment();
+ frag.appendChild(document_.createElement(SA::construct_from_utf8("foo")));
+
+ NodeSetVariableResolver svr;
+ NodeSet<string_type, string_adaptor> ns;
+ ns.push_back(frag);
+ svr.setVariable(SA::construct_from_utf8("fruit"), ns);
+ parser.setVariableResolver(svr);
+
+ XPathValue<string_type, string_adaptor> result = parser.evaluate_expr(SA::construct_from_utf8("$fruit/foo|/root/child3"), document_);
+ assert(NODE_SET == result.type());
+ assert(2 == result.asNodeSet().size());
+ assert(element3_ == result.asNodeSet()[0]);
+ } // testUnion11
+
+ if (false) {
+ using namespace Arabica::XPath;
+ XPathValue<string_type, string_adaptor> result = parser.evaluate_expr(SA::construct_from_utf8("local-name(/root)"), document_);
+ assert(STRING == result.type());
+ assert(SA::construct_from_utf8("root") == result.asString());
+ } // testLocalNameFn1
+
+ {
+ using namespace Arabica::XPath;
+ Arabica::DOM::DocumentFragment<std::string> frag = document_.createDocumentFragment();
+ frag.appendChild(document_.createElement("foo"));
+
+ NodeSetVariableResolver svr;
+ NodeSet<string_type, string_adaptor> ns;
+ ns.push_back(frag);
+ svr.setVariable("fruit", ns);
+ parser.setVariableResolver(svr);
+
+ XPathValue<string_type, string_adaptor> result = parser.evaluate(SA::construct_from_utf8("local-name($fruit/foo) = 'foo'"), document_);
+ std::cout << result.asBool() << std::endl;
+ }
+
+} \ No newline at end of file