summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2012-10-05 15:31:26 (GMT)
committerStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2012-10-05 15:31:26 (GMT)
commit0ae6c27d9322208053033d9b19c0ffffed3d99eb (patch)
tree0794b4df38568e03fb01e7fa91e6d4a625db859e
parent64cc2ce105cf57fcba637b309664b4bc74ae7d82 (diff)
downloaduscxml-0ae6c27d9322208053033d9b19c0ffffed3d99eb.zip
uscxml-0ae6c27d9322208053033d9b19c0ffffed3d99eb.tar.gz
uscxml-0ae6c27d9322208053033d9b19c0ffffed3d99eb.tar.bz2
Implemented DOM
-rw-r--r--CMakeLists.txt24
-rw-r--r--contrib/cmake/FindMiles.cmake116
-rw-r--r--contrib/cmake/FindUMUNDO.cmake112
-rw-r--r--src/uscxml/Factory.cpp9
-rw-r--r--src/uscxml/Interpreter.cpp113
-rw-r--r--src/uscxml/Interpreter.h39
-rw-r--r--src/uscxml/Message.cpp4
-rw-r--r--src/uscxml/Message.h2
-rw-r--r--src/uscxml/Utilities.cpp402
-rw-r--r--src/uscxml/Utilities.h52
-rw-r--r--src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp5
-rw-r--r--src/uscxml/datamodel/ecmascript/v8/V8DataModel.h7
-rw-r--r--src/uscxml/datamodel/ecmascript/v8/dom/V8SCXMLDOM.cpp188
-rw-r--r--src/uscxml/datamodel/ecmascript/v8/dom/V8SCXMLDOM.h58
-rw-r--r--src/uscxml/debug/SCXMLDotWriter.cpp358
-rw-r--r--src/uscxml/debug/SCXMLDotWriter.h45
-rw-r--r--src/uscxml/invoker/modality/MMIComponent.cpp42
-rw-r--r--src/uscxml/invoker/modality/MMIComponent.h109
-rw-r--r--src/uscxml/invoker/modality/UmundoComponent.cpp49
-rw-r--r--src/uscxml/invoker/modality/UmundoComponent.h30
-rw-r--r--src/uscxml/invoker/modality/miles/SpatialAudio.cpp153
-rw-r--r--src/uscxml/invoker/modality/miles/SpatialAudio.h49
-rw-r--r--src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp2
-rw-r--r--test/src/audio/click.wavbin0 -> 5058 bytes
-rw-r--r--test/src/scxml-gui-test.scxml13
-rw-r--r--test/src/test-completion.cpp4
-rw-r--r--test/src/test-dom.scxml20
-rw-r--r--test/src/test-spatial-audio.scxml68
28 files changed, 2012 insertions, 61 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fe9d754..e737a55 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -60,10 +60,34 @@ file(GLOB_RECURSE USCXML_DATAMODEL_V8
list (APPEND USCXML_FILES ${USCXML_DATAMODEL_V8})
list (APPEND USCXML_LIBS ${V8_LIBRARY})
+# curl
+find_package(CURL REQUIRED)
+include_directories(${CURL_INCLUDE_DIRS})
+list (APPEND USCXML_LIBS ${CURL_LIBRARIES})
+
+# umundo
+find_package(UMUNDO REQUIRED)
+include_directories(${UMUNDO_INCLUDE_DIR})
+list (APPEND USCXML_LIBS ${UMUNDO_LIBRARIES})
+
+# miles
+find_package(MILES COMPONENTS core audio debug REQUIRED)
+include_directories(${MILES_INCLUDE_DIR})
+list (APPEND USCXML_LIBS ${MILES_LIBRARIES})
+
+# openal
+find_package(OpenAL REQUIRED)
+include_directories(${OPENAL_INCLUDE_DIR})
+list(APPEND USCXML_LIBS ${OPENAL_LIBRARY})
+
+
# the invokers for external services
file(GLOB_RECURSE USCXML_INVOKER src/uscxml/invoker/*.cpp src/uscxml/invoker/*.h)
list (APPEND USCXML_FILES ${USCXML_INVOKER})
+# debug
+file(GLOB_RECURSE USCXML_DEBUG src/uscxml/debug/*.cpp src/uscxml/debug/*.h)
+list (APPEND USCXML_FILES ${USCXML_DEBUG})
file(GLOB USCXML_CONCURRENCY src/uscxml/concurrency/*.cpp src/uscxml/concurrency/*.h)
list (APPEND USCXML_FILES ${USCXML_CONCURRENCY})
diff --git a/contrib/cmake/FindMiles.cmake b/contrib/cmake/FindMiles.cmake
new file mode 100644
index 0000000..3166050
--- /dev/null
+++ b/contrib/cmake/FindMiles.cmake
@@ -0,0 +1,116 @@
+# - Find Miles
+# This module checks if Miles is installed and determines where the
+# include files and libraries are. This code sets the following
+# variables:
+#
+# MILES_INCLUDE_DIR = The full path to the miles headers
+# MILES_LIBRARIES = All miles libraries for release and debug builds
+#
+# Example:
+# find_package(MILES REQUIRED audio network)
+# include_directories(${MILES_INCLUDE_DIR})
+#
+
+# is this a 64Bit host?
+# if (NOT APPLE)
+# if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+# set(_MILES_64BIT_LIB_POSTFIX 64)
+# else()
+# set(_MILES_64BIT_LIB_POSTFIX "")
+# endif()
+# endif()
+
+###################################################
+# where to search for miles headers and libraries
+###################################################
+set(_MILES_LIB_SEARCHPATH
+ "/usr/local"
+ "/opt/local"
+ "C:/Program Files (x86)/miles"
+ "C:/Program Files/miles"
+)
+
+###################################################
+# get a list of components the user requested
+###################################################
+set(_MILES_COMPONENTS_TO_PROCESS)
+foreach(_MILES_COMPONENT ${MILES_FIND_COMPONENTS})
+ STRING(TOUPPER ${_MILES_COMPONENT} _MILES_COMPONENT_UC)
+ list(APPEND _MILES_COMPONENTS_TO_PROCESS ${_MILES_COMPONENT_UC})
+endforeach()
+list(APPEND _MILES_COMPONENTS_TO_PROCESS "CORE")
+list(REMOVE_DUPLICATES _MILES_COMPONENTS_TO_PROCESS)
+
+###################################################
+# find the miles header files
+###################################################
+FIND_PATH(MILES_INCLUDE_DIR miles/miles.h
+ PATH_SUFFIXES include
+ PATHS ${_MILES_LIB_SEARCHPATH}
+ ENV MILES_INCLUDE_DIR
+)
+
+###################################################
+# iterate components and try to find libraries
+# in debug and release configuration. For release
+# we prefer MinSizeRel, for debug we prefer
+# RelWithDebInfo.
+###################################################
+SET(MILES_LIBRARIES)
+foreach (_MILES_COMPONENT ${_MILES_COMPONENTS_TO_PROCESS})
+ SET(_CURR_COMPONENT "MILES_${_MILES_COMPONENT}_LIBRARY")
+ STRING(TOLOWER ${_MILES_COMPONENT}${_MILES_64BIT_LIB_POSTFIX} _MILES_COMPONENT_LC)
+
+ # prefer MinSizeRel libraries
+ FIND_LIBRARY(${_CURR_COMPONENT}
+ PATH_SUFFIXES lib
+ NAMES miles_${_MILES_COMPONENT_LC}_s
+ PATHS ${_MILES_LIB_SEARCHPATH}
+ ENV MILES_LIB_DIR
+ )
+ if (${_CURR_COMPONENT})
+ list(APPEND MILES_LIBRARIES optimized ${${_CURR_COMPONENT}})
+ else()
+ # if no minsize libraries were found try normal release
+ FIND_LIBRARY(${_CURR_COMPONENT}
+ PATH_SUFFIXES lib
+ NAMES miles_${_MILES_COMPONENT_LC}
+ PATHS ${_MILES_LIB_SEARCHPATH}
+ ENV MILES_LIB_DIR
+ )
+ if (${_CURR_COMPONENT})
+ list(APPEND MILES_LIBRARIES optimized ${${_CURR_COMPONENT}})
+ endif()
+ endif()
+
+ # prefer RelWithDebInfo libraries
+ FIND_LIBRARY(${_CURR_COMPONENT}_DEBUG
+ PATH_SUFFIXES lib
+ NAMES miles_${_MILES_COMPONENT_LC}_rd
+ PATHS ${_MILES_LIB_SEARCHPATH}
+ ENV MILES_LIB_DIR
+ )
+ if (${_CURR_COMPONENT}_DEBUG)
+ list(APPEND MILES_LIBRARIES debug ${${_CURR_COMPONENT}_DEBUG})
+ else()
+ message("Searching miles_${_MILES_COMPONENT_LC}_d")
+ FIND_LIBRARY(${_CURR_COMPONENT}_DEBUG
+ PATH_SUFFIXES lib
+ NAMES miles_${_MILES_COMPONENT_LC}_d
+ PATHS ${_MILES_LIB_SEARCHPATH}
+ ENV MILES_LIB_DIR
+ )
+ if (${_CURR_COMPONENT}_DEBUG)
+ list(APPEND MILES_LIBRARIES debug ${${_CURR_COMPONENT}_DEBUG})
+ endif()
+ endif()
+
+ if (NOT ${_CURR_COMPONENT} AND NOT ${_CURR_COMPONENT}_DEBUG)
+ message(FATAL_ERROR "Could not find miles component ${_MILES_COMPONENT}")
+ endif()
+endforeach()
+
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(MILES DEFAULT_MSG MILES_LIBRARIES MILES_INCLUDE_DIR)
+MARK_AS_ADVANCED(MILES_INCLUDE_DIR MILES_LIBRARIES)
diff --git a/contrib/cmake/FindUMUNDO.cmake b/contrib/cmake/FindUMUNDO.cmake
new file mode 100644
index 0000000..06df54a
--- /dev/null
+++ b/contrib/cmake/FindUMUNDO.cmake
@@ -0,0 +1,112 @@
+# - Find UMUNDO
+# This module checks if UMundo is installed and determines where the
+# include files and libraries are. This code sets the following
+# variables:
+#
+# UMUNDO_INCLUDE_DIR = The full path to the umundo headers
+# UMUNDO_LIBRARIES = All umundo libraries for release and debug builds
+#
+# Example:
+# find_package(UMUNDO REQUIRED util serial rpc)
+# include_directories(${UMUNDO_INCLUDE_DIR})
+#
+
+# is this a 64Bit host?
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(64BIT_LIB_POSTFIX 64)
+else()
+ set(64BIT_LIB_POSTFIX "")
+endif()
+
+###################################################
+# where to search for umundo headers and libraries
+###################################################
+set(_UMUNDO_LIB_SEARCHPATH
+ "/usr/local"
+ "/opt/local"
+ "C:/Program Files (x86)/uMundo"
+)
+
+###################################################
+# get a list of components the user requested
+###################################################
+set(_UMUNDO_COMPONENTS_TO_PROCESS)
+foreach(_UMUNDO_COMPONENT ${UMUNDO_FIND_COMPONENTS})
+ STRING(TOUPPER ${_UMUNDO_COMPONENT} _UMUNDO_COMPONENT_UC)
+ list(APPEND _UMUNDO_COMPONENTS_TO_PROCESS ${_UMUNDO_COMPONENT_UC})
+endforeach()
+list(APPEND _UMUNDO_COMPONENTS_TO_PROCESS "CORE")
+list(REMOVE_DUPLICATES _UMUNDO_COMPONENTS_TO_PROCESS)
+
+###################################################
+# find the umundo header files
+###################################################
+FIND_PATH(UMUNDO_INCLUDE_DIR umundo/core.h
+ PATH_SUFFIXES include
+ PATHS ${_UMUNDO_LIB_SEARCHPATH}
+ ENV UMUNDO_INCLUDE_DIR
+)
+
+###################################################
+# iterate components and try to find libraries
+# in debug and release configuration. For release
+# we prefer MinSizeRel, for debug we prefer
+# RelWithDebInfo.
+###################################################
+SET(UMUNDO_LIBRARIES)
+foreach (_UMUNDO_COMPONENT ${_UMUNDO_COMPONENTS_TO_PROCESS})
+ SET(_CURR_COMPONENT "UMUNDO_${_UMUNDO_COMPONENT}_LIBRARY")
+ STRING(TOLOWER ${_UMUNDO_COMPONENT}${64BIT_LIB_POSTFIX} _UMUNDO_COMPONENT_LC)
+
+ # prefer MinSizeRel libraries
+ FIND_LIBRARY(${_CURR_COMPONENT}
+ PATH_SUFFIXES lib
+ NAMES umundo${_UMUNDO_COMPONENT_LC}_s
+ PATHS ${_UMUNDO_LIB_SEARCHPATH}
+ ENV UMUNDO_LIB_DIR
+ )
+ if (${_CURR_COMPONENT})
+ list(APPEND UMUNDO_LIBRARIES optimized ${${_CURR_COMPONENT}})
+ else()
+ # if no minsize libraries were found try normal release
+ FIND_LIBRARY(${_CURR_COMPONENT}
+ PATH_SUFFIXES lib
+ NAMES umundo${_UMUNDO_COMPONENT_LC}
+ PATHS ${_UMUNDO_LIB_SEARCHPATH}
+ ENV UMUNDO_LIB_DIR
+ )
+ if (${_CURR_COMPONENT})
+ list(APPEND UMUNDO_LIBRARIES optimized ${${_CURR_COMPONENT}})
+ endif()
+ endif()
+
+ # prefer RelWithDebInfo libraries
+ FIND_LIBRARY(${_CURR_COMPONENT}_DEBUG
+ PATH_SUFFIXES lib
+ NAMES umundo${_UMUNDO_COMPONENT_LC}_rd
+ PATHS ${_UMUNDO_LIB_SEARCHPATH}
+ ENV UMUNDO_LIB_DIR
+ )
+ if (${_CURR_COMPONENT}_DEBUG)
+ list(APPEND UMUNDO_LIBRARIES debug ${${_CURR_COMPONENT}_DEBUG})
+ else()
+ FIND_LIBRARY(${_CURR_COMPONENT}_DEBUG
+ PATH_SUFFIXES lib
+ NAMES umundo${_UMUNDO_COMPONENT_LC}_d
+ PATHS ${_UMUNDO_LIB_SEARCHPATH}
+ ENV UMUNDO_LIB_DIR
+ )
+ if (${_CURR_COMPONENT}_DEBUG)
+ list(APPEND UMUNDO_LIBRARIES debug ${${_CURR_COMPONENT}_DEBUG})
+ endif()
+ endif()
+
+ if (NOT ${_CURR_COMPONENT} AND NOT ${_CURR_COMPONENT}_DEBUG)
+ message(FATAL_ERROR "Could not find umundo component ${_UMUNDO_COMPONENT}")
+ endif()
+endforeach()
+
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(UMUNDO DEFAULT_MSG UMUNDO_LIBRARIES UMUNDO_INCLUDE_DIR)
+MARK_AS_ADVANCED(UMUNDO_INCLUDE_DIR UMUNDO_LIBRARIES)
diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp
index b2346c2..36e0523 100644
--- a/src/uscxml/Factory.cpp
+++ b/src/uscxml/Factory.cpp
@@ -3,17 +3,24 @@
//#include "uscxml/ioprocessor/basichttp/pion/PionIOProcessor.h"
#include "uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.h"
#include "uscxml/invoker/scxml/USCXMLInvoker.h"
+#include "uscxml/invoker/modality/miles/SpatialAudio.h"
namespace uscxml {
Factory::Factory() {
_dataModels["ecmascript"] = new V8DataModel();
// _ioProcessors["basichttp"] = new PionIOProcessor();
- _ioProcessors["basichttp"] = new EventIOProcessor();
+
// use basichttp for transporting to/from scxml sessions as well
+ _ioProcessors["basichttp"] = new EventIOProcessor();
_ioProcessors["http://www.w3.org/TR/scxml/#SCXMLEventProcessor"] = _ioProcessors["basichttp"];
+
_invoker["scxml"] = new USCXMLInvoker();
_invoker["http://www.w3.org/TR/scxml/"] = _invoker["scxml"];
+
+ _invoker["spatial-audio"] = new SpatialAudio();
+ _invoker["http://www.smartvortex.eu/mmi/spatial-audio/"] = _invoker["spatial-audio"];
+
}
void Factory::registerIOProcessor(const std::string type, IOProcessor* ioProcessor) {
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index f965e1e..fbc048f 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -1,4 +1,5 @@
#include "uscxml/Interpreter.h"
+#include "uscxml/debug/SCXMLDotWriter.h"
#include <DOM/Simple/DOMImplementation.hpp>
@@ -127,10 +128,11 @@ void Interpreter::init() {
NodeList<std::string> scxmls = _doc.getElementsByTagName("scxml");
if (scxmls.getLength() > 0) {
_scxml = (Arabica::DOM::Element<std::string>)scxmls.item(0);
+ normalize(_doc);
+ _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : getUUID());
} else {
LOG(ERROR) << "Cannot find SCXML element" << std::endl;
}
- _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : getUUID());
}
}
@@ -170,7 +172,6 @@ void Interpreter::interpret() {
// dump();
_sessionId = getUUID();
- normalize(_doc);
if(HAS_ATTR(_scxml, "datamodel")) {
_dataModel = Factory::getDataModel(ATTR(_scxml, "datamodel"), this);
@@ -208,21 +209,10 @@ void Interpreter::interpret() {
}
}
- // executeTransitionContent
- Arabica::DOM::Element<std::string> initialState = (Arabica::DOM::Element<std::string>)getInitialState();
-
- // create a pseudo initial and transition element
- NodeSet<std::string> initialTransitions;
- Arabica::DOM::Element<std::string> initialElem = _doc.createElement("initial");
- Arabica::DOM::Element<std::string> transitionElem = _doc.createElement("transition");
- transitionElem.setAttribute("target", initialState.getAttribute("id"));
-
- initialElem.appendChild(transitionElem);
- _scxml.appendChild(initialElem);
-
- assert(boost::iequals(TAGNAME(initialElem), "initial"));
-
- initialTransitions.push_back(transitionElem);
+ // we made sure during normalization that this element exists
+ NodeSet<std::string> initialTransitions = _xpath.evaluate("/" + _nsPrefix + "scxml/" + _nsPrefix + "initial/" + _nsPrefix + "transition", _doc).asNodeSet();
+ assert(initialTransitions.size() > 0);
+ initialTransitions.push_back(initialTransitions[0]);
enterStates(initialTransitions);
mainEventLoop();
@@ -253,7 +243,7 @@ void Interpreter::initializeData(const Arabica::DOM::Node<std::string>& data) {
NodeList<std::string> dataChilds = data.getChildNodes();
for (int i = 0; i < dataChilds.getLength(); i++) {
if (dataChilds.item(i).getNodeType() == Node_base::TEXT_NODE) {
- Data value = Data(dataChilds.item(i), Data::INTERPRETED);
+ Data value = Data(dataChilds.item(i).getNodeValue());
_dataModel->assign(ATTR(data, "id"), value);
break;
}
@@ -266,7 +256,7 @@ void Interpreter::initializeData(const Arabica::DOM::Node<std::string>& data) {
}
}
-void Interpreter::normalize(const Arabica::DOM::Node<std::string>& node) {
+void Interpreter::normalize(const Arabica::DOM::Document<std::string>& node) {
// make sure every state has an id and set isFirstEntry to true
Arabica::XPath::NodeSet<std::string> states = _xpath.evaluate("//" + _nsPrefix + "state", _doc).asNodeSet();
for (int i = 0; i < states.size(); i++) {
@@ -313,6 +303,15 @@ void Interpreter::normalize(const Arabica::DOM::Node<std::string>& node) {
if (!((Arabica::DOM::Element<std::string>)scxml[0]).hasAttribute("id")) {
((Arabica::DOM::Element<std::string>)scxml[0]).setAttribute("id", getUUID());
}
+
+ // create a pseudo initial and transition element
+ Arabica::DOM::Element<std::string> initialState = (Arabica::DOM::Element<std::string>)getInitialState();
+ Arabica::DOM::Element<std::string> initialElem = _doc.createElement("initial");
+ Arabica::DOM::Element<std::string> transitionElem = _doc.createElement("transition");
+ transitionElem.setAttribute("target", initialState.getAttribute("id"));
+ initialElem.appendChild(transitionElem);
+ _scxml.appendChild(initialElem);
+
}
void Interpreter::mainEventLoop() {
@@ -323,7 +322,7 @@ void Interpreter::mainEventLoop() {
// Here we handle eventless transitions and transitions
// triggered by internal events until machine is stable
while(_running && !_stable) {
-#if 1
+#if 0
std::cout << "Configuration: ";
for (int i = 0; i < _configuration.size(); i++) {
std::cout << ((Arabica::DOM::Element<std::string>)_configuration[i]).getAttribute("id") << ", ";
@@ -337,10 +336,11 @@ void Interpreter::mainEventLoop() {
} else {
Event internalEvent = _internalQueue.front();
_internalQueue.pop_front();
-#if 1
+#if 0
std::cout << "Received internal event " << internalEvent.name << std::endl;
#endif
- _dataModel->setEvent(internalEvent);
+ if (_dataModel)
+ _dataModel->setEvent(internalEvent);
enabledTransitions = selectTransitions(internalEvent.name);
}
}
@@ -553,8 +553,7 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
}
std::string paramValue;
if (HAS_ATTR(params[i], "expr") && _dataModel) {
- std::string location = _dataModel->evalAsString(ATTR(params[i], "expr"));
- paramValue = _dataModel->evalAsString(location);
+ paramValue = _dataModel->evalAsString(ATTR(params[i], "expr"));
} else if(HAS_ATTR(params[i], "location") && _dataModel) {
paramValue = _dataModel->evalAsString(ATTR(params[i], "location"));
} else {
@@ -597,12 +596,23 @@ void Interpreter::delayedSend(void* userdata, std::string eventName) {
Interpreter* THIS = data->first;
SendRequest sendReq = data->second;
- if (boost::iequals(sendReq.target, "_parent")) {
+ if (boost::iequals(sendReq.target, "#_parent")) {
+ // send to parent scxml session
if (THIS->_invoker != NULL) {
THIS->_invoker->sendToParent(sendReq);
} else {
LOG(ERROR) << "Can not send to parent, we were not invoked" << std::endl;
}
+ } else if (sendReq.target.find_first_of("#_") == 0) {
+ // send to invoker
+ std::string invokeId = sendReq.target.substr(2, sendReq.target.length() - 2);
+ if (THIS->_invokerIds.find(invokeId) != THIS->_invokerIds.end()) {
+ THIS->_invokerIds[invokeId]->send(sendReq);
+ } else {
+ LOG(ERROR) << "Can not send to invoked component " << invokeId << ", no such invokeId" << std::endl;
+ }
+ } else if (sendReq.target.length() == 0) {
+ THIS->receive(sendReq);
} else {
IOProcessor* ioProc = THIS->getIOProcessor(sendReq.type);
if (ioProc != NULL) {
@@ -674,13 +684,17 @@ void Interpreter::invoke(const Arabica::DOM::Node<std::string>& element) {
// params
NodeSet<std::string> params = _xpath.evaluate("" + _nsPrefix + "param", element).asNodeSet();
for (int i = 0; i < params.size(); i++) {
- if (HAS_ATTR(params[i], "name")) {
+ if (!HAS_ATTR(params[i], "name")) {
LOG(ERROR) << "param element is missing name attribut";
continue;
}
std::string paramValue;
- if (HAS_ATTR(params[i], "expr") && _dataModel) {
- paramValue = _dataModel->evalAsString(ATTR(params[i], "expr"));
+ if (HAS_ATTR(params[i], "expr")) {
+ if (_dataModel) {
+ paramValue = _dataModel->evalAsString(ATTR(params[i], "expr"));
+ } else {
+ paramValue = ATTR(params[i], "expr");
+ }
} else if(HAS_ATTR(params[i], "location") && _dataModel) {
paramValue = _dataModel->evalAsString(ATTR(params[i], "location"));
} else {
@@ -859,7 +873,7 @@ bool Interpreter::isPreemptingTransition(const Arabica::DOM::Node<std::string>&
}
void Interpreter::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) {
-#if 1
+#if 0
std::cout << "Transitions: ";
for (int i = 0; i < enabledTransitions.size(); i++) {
std::cout << ((Arabica::DOM::Element<std::string>)getSourceState(enabledTransitions[i])).getAttribute("id") << " -> " << std::endl;
@@ -1374,14 +1388,13 @@ void Interpreter::addStatesToEnter(const Arabica::DOM::Node<std::string>& state,
Arabica::XPath::NodeSet<std::string> Interpreter::getChildStates(const Arabica::DOM::Node<std::string>& state) {
Arabica::XPath::NodeSet<std::string> childs;
- Arabica::XPath::NodeSet<std::string> stateChilds = _xpath.evaluate("" + _nsPrefix + "state", state).asNodeSet();
- Arabica::XPath::NodeSet<std::string> parallelChilds = _xpath.evaluate("" + _nsPrefix + "parallel", state).asNodeSet();
- Arabica::XPath::NodeSet<std::string> finalChilds = _xpath.evaluate("" + _nsPrefix + "final", state).asNodeSet();
- childs.insert(childs.begin(), stateChilds.begin(), stateChilds.end());
- childs.insert(childs.begin(), parallelChilds.begin(), parallelChilds.end());
- childs.insert(childs.begin(), finalChilds.begin(), finalChilds.end());
-
+ Arabica::DOM::NodeList<std::string> childElems = state.getChildNodes();
+ for (int i = 0; i < childElems.getLength(); i++) {
+ if (isState(childElems.item(i))) {
+ childs.push_back(childElems.item(i));
+ }
+ }
return childs;
}
@@ -1412,7 +1425,7 @@ Arabica::DOM::Node<std::string> Interpreter::findLCCA(const Arabica::XPath::Node
Arabica::DOM::Node<std::string> Interpreter::getState(const std::string& stateId) {
// first try atomic and compund states
- std::cout << _nsPrefix << stateId << std::endl;
+// std::cout << _nsPrefix << stateId << std::endl;
NodeSet<std::string> target = _xpath.evaluate("//" + _nsPrefix + "state[@id='" + stateId + "']", _doc).asNodeSet();
if (target.size() > 0)
goto FOUND;
@@ -1481,6 +1494,18 @@ Arabica::DOM::Node<std::string> Interpreter::getInitialState(Arabica::DOM::Node<
NodeSet<std::string> Interpreter::getTargetStates(const Arabica::DOM::Node<std::string>& transition) {
NodeSet<std::string> targetStates;
+
+ // if we are called with a state, process all its transitions
+ if (isState(transition)) {
+ NodeList<std::string> childs = transition.getChildNodes();
+ for (int i = 0; i < childs.getLength(); i++) {
+ if (childs.item(i).getNodeType() == Node_base::ELEMENT_NODE && boost::iequals(TAGNAME(childs.item(i)), "transition")) {
+ targetStates.push_back(getTargetStates(childs.item(i)));
+ }
+ }
+ return targetStates;
+ }
+
std::string targetId = ((Arabica::DOM::Element<std::string>)transition).getAttribute("target");
std::vector<std::string> targetIds = Interpreter::tokenizeIdRefs(ATTR(transition, "target"));
@@ -1581,6 +1606,20 @@ bool Interpreter::isFinal(const Arabica::DOM::Node<std::string>& state) {
return false;
}
+bool Interpreter::isInitial(const Arabica::DOM::Node<std::string>& state) {
+ if (!isState(state))
+ return false;
+
+ Arabica::DOM::Node<std::string> parent = state.getParentNode();
+ if (!isState(parent))
+ return true; // scxml element
+
+ if (getInitialState(parent) == state)
+ return true; // every nested node
+
+ return false;
+}
+
bool Interpreter::isPseudoState(const Arabica::DOM::Node<std::string>& state) {
std::string tagName = TAGNAME(state);
if (boost::iequals("initial", tagName))
diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h
index 4a948dc..964320d 100644
--- a/src/uscxml/Interpreter.h
+++ b/src/uscxml/Interpreter.h
@@ -48,6 +48,8 @@ namespace uscxml {
DataModel* getDataModel() { return _dataModel; }
Invoker* getInvoker() { return _invoker; }
void setInvoker(Invoker* invoker) { _invoker = invoker; }
+ std::string getNSPrefix() { return _nsPrefix; }
+ Arabica::XPath::XPath<std::string>& getXPath() { return _xpath; }
void waitForStabilization();
@@ -55,7 +57,8 @@ namespace uscxml {
void receiveInternal(Event& event) { _internalQueue.push_back(event); }
Arabica::XPath::NodeSet<std::string> getConfiguration() { return _configuration; }
Arabica::DOM::Node<std::string> getState(const std::string& stateId);
-
+ Arabica::DOM::Document<std::string>& getDocument() { return _doc; }
+
const std::string& getName() { return _name; }
const std::string& getSessionId() { return _sessionId; }
@@ -64,11 +67,27 @@ namespace uscxml {
void dump();
static void dump(const Arabica::DOM::Node<std::string>& node, int lvl = 0);
+ static bool isState(const Arabica::DOM::Node<std::string>& state);
+ static bool isPseudoState(const Arabica::DOM::Node<std::string>& state);
+ static bool isTransitionTarget(const Arabica::DOM::Node<std::string>& elem);
+ static bool isTargetless(const Arabica::DOM::Node<std::string>& transition);
+ static bool isAtomic(const Arabica::DOM::Node<std::string>& state);
+ static bool isFinal(const Arabica::DOM::Node<std::string>& state);
+ static bool isHistory(const Arabica::DOM::Node<std::string>& state);
+ static bool isParallel(const Arabica::DOM::Node<std::string>& state);
+ static bool isCompound(const Arabica::DOM::Node<std::string>& state);
+ static bool isDescendant(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2);
+
+ bool isInitial(const Arabica::DOM::Node<std::string>& state);
+ Arabica::DOM::Node<std::string> getInitialState(Arabica::DOM::Node<std::string> state = Arabica::DOM::Node<std::string>());
+ static Arabica::XPath::NodeSet<std::string> getChildStates(const Arabica::DOM::Node<std::string>& state);
+ Arabica::XPath::NodeSet<std::string> getTargetStates(const Arabica::DOM::Node<std::string>& transition);
+
protected:
Interpreter();
void init();
- void normalize(const Arabica::DOM::Node<std::string>& node);
+ void normalize(const Arabica::DOM::Document<std::string>& node);
void setupIOProcessors();
void mainEventLoop();
@@ -116,9 +135,6 @@ namespace uscxml {
Arabica::XPath::NodeSet<std::string> selectEventlessTransitions();
Arabica::XPath::NodeSet<std::string> selectTransitions(const std::string& event);
- Arabica::XPath::NodeSet<std::string> getTargetStates(const Arabica::DOM::Node<std::string>& transition);
- Arabica::XPath::NodeSet<std::string> getChildStates(const Arabica::DOM::Node<std::string>& state);
- Arabica::DOM::Node<std::string> getInitialState(Arabica::DOM::Node<std::string> state = Arabica::DOM::Node<std::string>());
Arabica::DOM::Node<std::string> getSourceState(const Arabica::DOM::Node<std::string>& transition);
Arabica::DOM::Node<std::string> findLCCA(const Arabica::XPath::NodeSet<std::string>& states);
static Arabica::XPath::NodeSet<std::string> getProperAncestors(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2);
@@ -138,18 +154,7 @@ namespace uscxml {
bool isInFinalState(const Arabica::DOM::Node<std::string>& state);
bool isWithinSameChild(const Arabica::DOM::Node<std::string>& transition);
bool parentIsScxmlState(Arabica::DOM::Node<std::string> state);
-
- static bool isState(const Arabica::DOM::Node<std::string>& state);
- static bool isPseudoState(const Arabica::DOM::Node<std::string>& state);
- static bool isTransitionTarget(const Arabica::DOM::Node<std::string>& elem);
- static bool isTargetless(const Arabica::DOM::Node<std::string>& transition);
- static bool isAtomic(const Arabica::DOM::Node<std::string>& state);
- static bool isFinal(const Arabica::DOM::Node<std::string>& state);
- static bool isHistory(const Arabica::DOM::Node<std::string>& state);
- static bool isParallel(const Arabica::DOM::Node<std::string>& state);
- static bool isCompound(const Arabica::DOM::Node<std::string>& state);
- static bool isDescendant(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2);
-
+
static std::vector<std::string> tokenizeIdRefs(const std::string& idRefs);
static boost::uuids::random_generator uuidGen;
diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp
index a2990b9..9b713ca 100644
--- a/src/uscxml/Message.cpp
+++ b/src/uscxml/Message.cpp
@@ -7,7 +7,7 @@ namespace uscxml {
static int _dataIndentation = 1;
-Data::Data(const Arabica::DOM::Node<std::string>& dom, Type type) {
+Data::Data(const Arabica::DOM::Node<std::string>& dom) {
// we may need to convert some keys to arrays if we have the same name as an element
std::map<std::string, std::list<Data> > arrays;
// Interpreter::dump(dom);
@@ -54,7 +54,7 @@ Data::Data(const Arabica::DOM::Node<std::string>& dom, Type type) {
}
} else {
atom = dom.getNodeValue();
- this->type = type;
+ type = VERBATIM;
}
std::map<std::string, std::list<Data> >::iterator arrayIter = arrays.begin();
diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h
index be7747d..34b85e1 100644
--- a/src/uscxml/Message.h
+++ b/src/uscxml/Message.h
@@ -24,7 +24,7 @@ public:
Data() {}
Data(const std::string& atom_, Type type_ = INTERPRETED) : atom(atom_), type(type_) {}
- Data(const Arabica::DOM::Node<std::string>& dom, Type type = VERBATIM);
+ Data(const Arabica::DOM::Node<std::string>& dom);
virtual ~Data() {}
static Data fromXML(const std::string& xmlString);
diff --git a/src/uscxml/Utilities.cpp b/src/uscxml/Utilities.cpp
new file mode 100644
index 0000000..04a376c
--- /dev/null
+++ b/src/uscxml/Utilities.cpp
@@ -0,0 +1,402 @@
+/*****************************************************************************
+ *
+ * Taken in its entirety from http://curl.haxx.se/libcurl/c/fopen.html
+ *
+ * This example source code introduces a c library buffered I/O interface to
+ * URL reads it supports fopen(), fread(), fgets(), feof(), fclose(),
+ * rewind(). Supported functions have identical prototypes to their normal c
+ * lib namesakes and are preceaded by url_ .
+ *
+ * Using this code you can replace your program's fopen() with url_fopen()
+ * and fread() with url_fread() and it become possible to read remote streams
+ * instead of (only) local files. Local files (ie those that can be directly
+ * fopened) will drop back to using the underlying clib implementations
+ *
+ * See the main() function at the bottom that shows an app that retrives from a
+ * specified url using fgets() and fread() and saves as two output files.
+ *
+ * Copyright (c) 2003 Simtec Electronics
+ *
+ * Re-implemented by Vincent Sanders <vince@kyllikki.org> with extensive
+ * reference to original curl example code
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This example requires libcurl 7.9.7 or later.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+
+#include "uscxml/Utilities.h"
+
+/* we use a global one for convenience */
+CURLM *multi_handle;
+
+/* curl calls this routine to get more data */
+static size_t write_callback(char *buffer,
+ size_t size,
+ size_t nitems,
+ void *userp)
+{
+ char *newbuff;
+ size_t rembuff;
+
+ URL_FILE *url = (URL_FILE *)userp;
+ size *= nitems;
+
+ rembuff=url->buffer_len - url->buffer_pos; /* remaining space in buffer */
+
+ if(size > rembuff) {
+ /* not enough space in buffer */
+ newbuff=(char*)realloc(url->buffer,url->buffer_len + (size - rembuff));
+ if(newbuff==NULL) {
+ fprintf(stderr,"callback buffer grow failed\n");
+ size=rembuff;
+ }
+ else {
+ /* realloc suceeded increase buffer size*/
+ url->buffer_len+=size - rembuff;
+ url->buffer=newbuff;
+ }
+ }
+
+ memcpy(&url->buffer[url->buffer_pos], buffer, size);
+ url->buffer_pos += size;
+
+ return size;
+}
+
+/* use to attempt to fill the read buffer up to requested number of bytes */
+static int fill_buffer(URL_FILE *file, size_t want)
+{
+ fd_set fdread;
+ fd_set fdwrite;
+ fd_set fdexcep;
+ struct timeval timeout;
+ int rc;
+
+ /* only attempt to fill buffer if transactions still running and buffer
+ * doesnt exceed required size already
+ */
+ if((!file->still_running) || (file->buffer_pos > want))
+ return 0;
+
+ /* attempt to fill buffer */
+ do {
+ int maxfd = -1;
+ long curl_timeo = -1;
+
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+ FD_ZERO(&fdexcep);
+
+ /* set a suitable timeout to fail on */
+ timeout.tv_sec = 60; /* 1 minute */
+ timeout.tv_usec = 0;
+
+ curl_multi_timeout(multi_handle, &curl_timeo);
+ if(curl_timeo >= 0) {
+ timeout.tv_sec = curl_timeo / 1000;
+ if(timeout.tv_sec > 1)
+ timeout.tv_sec = 1;
+ else
+ timeout.tv_usec = (curl_timeo % 1000) * 1000;
+ }
+
+ /* get file descriptors from the transfers */
+ curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
+
+ /* In a real-world program you OF COURSE check the return code of the
+ function calls. On success, the value of maxfd is guaranteed to be
+ greater or equal than -1. We call select(maxfd + 1, ...), specially
+ in case of (maxfd == -1), we call select(0, ...), which is basically
+ equal to sleep. */
+
+ rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
+
+ switch(rc) {
+ case -1:
+ /* select error */
+ break;
+
+ case 0:
+ default:
+ /* timeout or readable/writable sockets */
+ curl_multi_perform(multi_handle, &file->still_running);
+ break;
+ }
+ } while(file->still_running && (file->buffer_pos < want));
+ return 1;
+}
+
+/* use to remove want bytes from the front of a files buffer */
+static int use_buffer(URL_FILE *file,int want)
+{
+ /* sort out buffer */
+ if((file->buffer_pos - want) <=0) {
+ /* ditch buffer - write will recreate */
+ if(file->buffer)
+ free(file->buffer);
+
+ file->buffer=NULL;
+ file->buffer_pos=0;
+ file->buffer_len=0;
+ }
+ else {
+ /* move rest down make it available for later */
+ memmove(file->buffer,
+ &file->buffer[want],
+ (file->buffer_pos - want));
+
+ file->buffer_pos -= want;
+ }
+ return 0;
+}
+
+URL_FILE *url_fopen(const char *url,const char *operation)
+{
+ /* this code could check for URLs or types in the 'url' and
+ basicly use the real fopen() for standard files */
+
+ URL_FILE *file;
+ (void)operation;
+
+ file = (URL_FILE*)malloc(sizeof(URL_FILE));
+ if(!file)
+ return NULL;
+
+ memset(file, 0, sizeof(URL_FILE));
+
+ if((file->handle.file=fopen(url,operation)))
+ file->type = CFTYPE_FILE; /* marked as URL */
+
+ else {
+ file->type = CFTYPE_CURL; /* marked as URL */
+ file->handle.curl = curl_easy_init();
+
+ curl_easy_setopt(file->handle.curl, CURLOPT_URL, url);
+ curl_easy_setopt(file->handle.curl, CURLOPT_WRITEDATA, file);
+ curl_easy_setopt(file->handle.curl, CURLOPT_VERBOSE, 0L);
+ curl_easy_setopt(file->handle.curl, CURLOPT_WRITEFUNCTION, write_callback);
+
+ if(!multi_handle)
+ multi_handle = curl_multi_init();
+
+ curl_multi_add_handle(multi_handle, file->handle.curl);
+
+ /* lets start the fetch */
+ curl_multi_perform(multi_handle, &file->still_running);
+
+ if((file->buffer_pos == 0) && (!file->still_running)) {
+ /* if still_running is 0 now, we should return NULL */
+
+ /* make sure the easy handle is not in the multi handle anymore */
+ curl_multi_remove_handle(multi_handle, file->handle.curl);
+
+ /* cleanup */
+ curl_easy_cleanup(file->handle.curl);
+
+ free(file);
+
+ file = NULL;
+ }
+ }
+ return file;
+}
+
+int url_fclose(URL_FILE *file)
+{
+ int ret=0;/* default is good return */
+
+ switch(file->type) {
+ case CFTYPE_FILE:
+ ret=fclose(file->handle.file); /* passthrough */
+ break;
+
+ case CFTYPE_CURL:
+ /* make sure the easy handle is not in the multi handle anymore */
+ curl_multi_remove_handle(multi_handle, file->handle.curl);
+
+ /* cleanup */
+ curl_easy_cleanup(file->handle.curl);
+ break;
+
+ default: /* unknown or supported type - oh dear */
+ ret=EOF;
+ errno=EBADF;
+ break;
+ }
+
+ if(file->buffer)
+ free(file->buffer);/* free any allocated buffer space */
+
+ free(file);
+
+ return ret;
+}
+
+int url_feof(URL_FILE *file)
+{
+ int ret=0;
+
+ switch(file->type) {
+ case CFTYPE_FILE:
+ ret=feof(file->handle.file);
+ break;
+
+ case CFTYPE_CURL:
+ if((file->buffer_pos == 0) && (!file->still_running))
+ ret = 1;
+ break;
+
+ default: /* unknown or supported type - oh dear */
+ ret=-1;
+ errno=EBADF;
+ break;
+ }
+ return ret;
+}
+
+size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file)
+{
+ size_t want;
+
+ switch(file->type) {
+ case CFTYPE_FILE:
+ want=fread(ptr,size,nmemb,file->handle.file);
+ break;
+
+ case CFTYPE_CURL:
+ want = nmemb * size;
+
+ fill_buffer(file,want);
+
+ /* check if theres data in the buffer - if not fill_buffer()
+ * either errored or EOF */
+ if(!file->buffer_pos)
+ return 0;
+
+ /* ensure only available data is considered */
+ if(file->buffer_pos < want)
+ want = file->buffer_pos;
+
+ /* xfer data to caller */
+ memcpy(ptr, file->buffer, want);
+
+ use_buffer(file,want);
+
+ want = want / size; /* number of items */
+ break;
+
+ default: /* unknown or supported type - oh dear */
+ want=0;
+ errno=EBADF;
+ break;
+
+ }
+ return want;
+}
+
+char *url_fgets(char *ptr, size_t size, URL_FILE *file)
+{
+ size_t want = size - 1;/* always need to leave room for zero termination */
+ size_t loop;
+
+ switch(file->type) {
+ case CFTYPE_FILE:
+ ptr = fgets(ptr,size,file->handle.file);
+ break;
+
+ case CFTYPE_CURL:
+ fill_buffer(file,want);
+
+ /* check if theres data in the buffer - if not fill either errored or
+ * EOF */
+ if(!file->buffer_pos)
+ return NULL;
+
+ /* ensure only available data is considered */
+ if(file->buffer_pos < want)
+ want = file->buffer_pos;
+
+ /*buffer contains data */
+ /* look for newline or eof */
+ for(loop=0;loop < want;loop++) {
+ if(file->buffer[loop] == '\n') {
+ want=loop+1;/* include newline */
+ break;
+ }
+ }
+
+ /* xfer data to caller */
+ memcpy(ptr, file->buffer, want);
+ ptr[want]=0;/* allways null terminate */
+
+ use_buffer(file,want);
+
+ break;
+
+ default: /* unknown or supported type - oh dear */
+ ptr=NULL;
+ errno=EBADF;
+ break;
+ }
+
+ return ptr;/*success */
+}
+
+void url_rewind(URL_FILE *file)
+{
+ switch(file->type) {
+ case CFTYPE_FILE:
+ rewind(file->handle.file); /* passthrough */
+ break;
+
+ case CFTYPE_CURL:
+ /* halt transaction */
+ curl_multi_remove_handle(multi_handle, file->handle.curl);
+
+ /* restart */
+ curl_multi_add_handle(multi_handle, file->handle.curl);
+
+ /* ditch buffer - write will recreate - resets stream pos*/
+ if(file->buffer)
+ free(file->buffer);
+
+ file->buffer=NULL;
+ file->buffer_pos=0;
+ file->buffer_len=0;
+
+ break;
+
+ default: /* unknown or supported type - oh dear */
+ break;
+ }
+}
+ \ No newline at end of file
diff --git a/src/uscxml/Utilities.h b/src/uscxml/Utilities.h
new file mode 100644
index 0000000..d8fd0f8
--- /dev/null
+++ b/src/uscxml/Utilities.h
@@ -0,0 +1,52 @@
+#ifndef UTILITIES_H_A89E99LI
+#define UTILITIES_H_A89E99LI
+
+#include <string>
+#include <sstream>
+#include <curl/curl.h>
+
+// see http://stackoverflow.com/questions/228005/alternative-to-itoa-for-converting-integer-to-string-c
+template <typename T> std::string toStr(T tmp) {
+ std::ostringstream out;
+ out << tmp;
+ return out.str();
+}
+
+template <typename T> T strTo(std::string tmp) {
+ T output;
+ std::istringstream in(tmp);
+ in >> output;
+ return output;
+}
+
+
+enum fcurl_type_e {
+ CFTYPE_NONE=0,
+ CFTYPE_FILE=1,
+ CFTYPE_CURL=2
+};
+
+struct fcurl_data
+{
+ enum fcurl_type_e type; /* type of handle */
+ union {
+ CURL *curl;
+ FILE *file;
+ } handle; /* handle */
+
+ char *buffer; /* buffer to store cached data*/
+ size_t buffer_len; /* currently allocated buffers length */
+ size_t buffer_pos; /* end of data in buffer*/
+ int still_running; /* Is background url fetch still in progress */
+};
+
+typedef struct fcurl_data URL_FILE;
+
+URL_FILE *url_fopen(const char *url,const char *operation);
+int url_fclose(URL_FILE *file);
+int url_feof(URL_FILE *file);
+size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file);
+char * url_fgets(char *ptr, size_t size, URL_FILE *file);
+void url_rewind(URL_FILE *file);
+
+#endif /* end of include guard: UTILITIES_H_A89E99LI */
diff --git a/src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp
index 52c7ad7..28ed8c1 100644
--- a/src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp
+++ b/src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp
@@ -1,4 +1,5 @@
#include "uscxml/datamodel/ecmascript/v8/V8DataModel.h"
+#include "dom/V8SCXMLDOM.h"
#include "uscxml/Message.h"
namespace uscxml {
@@ -19,11 +20,15 @@ DataModel* V8DataModel::create(Interpreter* interpreter) {
v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
global->Set(v8::String::New("In"), v8::FunctionTemplate::New(jsIn, v8::External::New(reinterpret_cast<void*>(dm))));
+
+ dm->_dom = new V8SCXMLDOM(interpreter);
+ global->Set(v8::String::New("document"), dm->_dom->getDocument());
dm->_contexts.push_back(v8::Context::New(NULL, global));
dm->setName(interpreter->getName());
dm->setSessionId(interpreter->getSessionId());
dm->eval("_ioprocessors = {};");
+
return dm;
}
diff --git a/src/uscxml/datamodel/ecmascript/v8/V8DataModel.h b/src/uscxml/datamodel/ecmascript/v8/V8DataModel.h
index 340b6ee..b8bfa6d 100644
--- a/src/uscxml/datamodel/ecmascript/v8/V8DataModel.h
+++ b/src/uscxml/datamodel/ecmascript/v8/V8DataModel.h
@@ -6,8 +6,9 @@
#include <v8.h>
namespace uscxml {
- class Event;
- class Data;
+ class Event;
+ class Data;
+ class V8SCXMLDOM;
}
namespace uscxml {
@@ -59,6 +60,8 @@ protected:
std::string _sessionId;
std::string _name;
+ V8SCXMLDOM* _dom;
+
Event _event;
v8::Persistent<v8::ObjectTemplate> _globalTemplate;
v8::Persistent<v8::ObjectTemplate> _eventTemplate;
diff --git a/src/uscxml/datamodel/ecmascript/v8/dom/V8SCXMLDOM.cpp b/src/uscxml/datamodel/ecmascript/v8/dom/V8SCXMLDOM.cpp
new file mode 100644
index 0000000..390d110
--- /dev/null
+++ b/src/uscxml/datamodel/ecmascript/v8/dom/V8SCXMLDOM.cpp
@@ -0,0 +1,188 @@
+#include "V8SCXMLDOM.h"
+
+namespace uscxml {
+
+ using namespace Arabica::DOM;
+ using namespace Arabica::XPath;
+
+ V8SCXMLDOM::V8SCXMLDOM(Interpreter* interpreter) {
+ _interpreter = interpreter;
+ }
+
+ v8::Handle<v8::ObjectTemplate> V8SCXMLDOM::getDocument() {
+ v8::Handle<v8::ObjectTemplate> documentTmpl = v8::ObjectTemplate::New();
+ documentTmpl->Set(v8::String::New("createElement"), v8::FunctionTemplate::New(jsDocumentCreateElement, v8::External::New(reinterpret_cast<void*>(_interpreter))));
+ documentTmpl->Set(v8::String::New("evaluate"), v8::FunctionTemplate::New(jsDocumentEvaluate, v8::External::New(reinterpret_cast<void*>(_interpreter))));
+ return documentTmpl;
+ }
+
+ v8::Handle<v8::Value> V8SCXMLDOM::jsDocumentCreateElement(const v8::Arguments& args) {
+ assert(!args.Data().IsEmpty());
+ assert(args.Data()->IsExternal());
+
+ Interpreter* interpreter = static_cast<Interpreter*>(v8::External::Unwrap(args.Data()));
+ v8::Handle<v8::ObjectTemplate> elementTmpl = v8::ObjectTemplate::New();
+ elementTmpl->SetAccessor(v8::String::New("tagName"), V8SCXMLDOM::jsElementTagName);
+ elementTmpl->Set(v8::String::New("getAttribute"), v8::FunctionTemplate::New(jsElementGetAttribute));
+ elementTmpl->Set(v8::String::New("setAttribute"), v8::FunctionTemplate::New(jsElementSetAttribute));
+ elementTmpl->SetInternalFieldCount(1);
+ v8::Handle<v8::Object> elementJS = elementTmpl->NewInstance();
+
+ assert(args.Length() == 1);
+ assert(args[0]->IsString());
+
+ v8::String::AsciiValue tagName(args[0]);
+ Element<std::string>* element = new Element<std::string>(interpreter->getDocument().createElement(*tagName));
+
+ elementJS->SetInternalField(0, v8::External::New(element));
+ return elementJS;
+ }
+
+ v8::Handle<v8::Value> V8SCXMLDOM::jsDocumentEvaluate(const v8::Arguments& args) {
+ assert(!args.Data().IsEmpty());
+ assert(args.Data()->IsExternal());
+
+ assert(args.Length() > 0);
+ assert(args[0]->IsString());
+
+
+ Interpreter* interpreter = static_cast<Interpreter*>(v8::External::Unwrap(args.Data()));
+ Node<std::string> context;
+ if (args.Length() > 1) {
+ assert(args[1]->ToObject()->InternalFieldCount() == 1);
+ context = *static_cast<Node<std::string>*>(v8::Local<v8::External>::Cast(args[1]->ToObject()->GetInternalField(0))->Value());
+ } else {
+ context = interpreter->getDocument();
+ }
+ v8::String::AsciiValue xpathExpr(args[0]);
+ XPathValue<std::string>* xpathValue = new XPathValue<std::string>(interpreter->getXPath().evaluate(*xpathExpr, context));
+
+ v8::Handle<v8::Object> xpathValueJS = getXPathValueTmpl()->NewInstance();
+ xpathValueJS->SetInternalField(0, v8::External::New(xpathValue));
+ return xpathValueJS;
+ }
+
+ v8::Handle<v8::Value> V8SCXMLDOM::jsElementTagName(v8::Local<v8::String> property,
+ const v8::AccessorInfo &info) {
+ Element<std::string>* element = static_cast<Element<std::string>*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value());
+ return v8::String::New(element->getTagName().c_str());
+ }
+
+ v8::Handle<v8::Value> V8SCXMLDOM::jsElementGetAttribute(const v8::Arguments& args) {
+ assert(!args.Data().IsEmpty());
+ assert(args.Data()->IsExternal());
+
+ assert(args.Length() == 1);
+ assert(args[0]->IsString());
+
+ Element<std::string>* element = static_cast<Element<std::string>*>(v8::External::Unwrap(args.Data()));
+
+ v8::String::AsciiValue attribute(args[0]);
+ if (element->hasAttribute(*attribute)) {
+ return v8::String::New(element->getAttribute(*attribute).c_str());
+ }
+ return v8::String::New("");
+ }
+
+ v8::Handle<v8::Value> V8SCXMLDOM::jsElementSetAttribute(const v8::Arguments& args) {
+ v8::Local<v8::Object> self = args.Holder();
+ assert(self->InternalFieldCount() == 1);
+
+ assert(args.Length() == 2);
+ assert(args[0]->IsString());
+ assert(args[1]->IsString());
+
+ v8::String::AsciiValue attribute(args[0]);
+ v8::String::AsciiValue value(args[1]);
+
+ Element<std::string>* element = static_cast<Element<std::string>*>(v8::External::Unwrap(self->GetInternalField(0)));
+ element->setAttribute(*attribute, *value);
+ return v8::Undefined();
+ }
+
+ v8::Handle<v8::Value> V8SCXMLDOM::jsXPathValueAsNodeSet(const v8::Arguments& args) {
+ v8::Local<v8::Object> self = args.Holder();
+ assert(self->InternalFieldCount() == 1);
+ XPathValue<std::string>* xPathValue = static_cast<XPathValue<std::string>*>(v8::External::Unwrap(self->GetInternalField(0)));
+
+ v8::Persistent<v8::Object> nodeSetJS = v8::Persistent<v8::Object>::New(getNodeSetTmpl()->NewInstance());
+ nodeSetJS->SetInternalField(0, v8::External::New(new NodeSet<std::string>(xPathValue->asNodeSet())));
+ nodeSetJS.MakeWeak(NULL, jsNodeSetDestructor);
+ return nodeSetJS;
+
+ }
+
+ void V8SCXMLDOM::jsNodeSetDestructor(v8::Persistent<v8::Value> object, void* data) {
+ NodeSet<std::string>* nodeSet = static_cast<NodeSet<std::string>*>(v8::Local<v8::External>::Cast(object->ToObject()->GetInternalField(0))->Value());
+ delete nodeSet;
+ }
+
+ v8::Handle<v8::Value> V8SCXMLDOM::jsNodeSetGetIndex(uint32_t index, const v8::AccessorInfo &info) {
+ v8::Local<v8::Object> self = info.Holder();
+ assert(self->InternalFieldCount() == 1);
+ NodeSet<std::string>* nodeSet = static_cast<NodeSet<std::string>*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value());
+
+ if (nodeSet->size() >= index) {
+ Node<std::string>* node = new Node<std::string>((*nodeSet)[index]);
+ v8::Handle<v8::Object> nodeJS = getNodeTmpl()->NewInstance();
+ nodeJS->SetInternalField(0, v8::External::New(node));
+ return nodeJS;
+ }
+ return v8::Undefined();
+ }
+
+ v8::Handle<v8::Value> V8SCXMLDOM::jsNodeSetLength(const v8::Arguments& args) {
+ v8::Local<v8::Object> self = args.Holder();
+ assert(self->InternalFieldCount() == 1);
+ NodeSet<std::string>* nodeSet = static_cast<NodeSet<std::string>*>(v8::External::Unwrap(self->GetInternalField(0)));
+ return v8::Integer::New(nodeSet->size());
+ }
+
+ v8::Handle<v8::Value> V8SCXMLDOM::jsNodeAppendChild(const v8::Arguments& args) {
+ v8::Local<v8::Object> self = args.Holder();
+ assert(self->InternalFieldCount() == 1);
+ Node<std::string>* node = static_cast<Node<std::string>*>(v8::External::Unwrap(self->GetInternalField(0)));
+
+ assert(args.Length() == 1);
+ assert(args[0]->IsObject());
+
+ Node<std::string>* childToAppend = static_cast<Node<std::string>*>(v8::External::Unwrap(args[0]->ToObject()->GetInternalField(0)));
+ node->appendChild(*childToAppend);
+
+// std::cout << *childToAppend << std::endl;
+
+ return v8::Undefined();
+ }
+
+ v8::Handle<v8::ObjectTemplate> V8SCXMLDOM::xPathValueTmpl;
+ v8::Handle<v8::ObjectTemplate> V8SCXMLDOM::getXPathValueTmpl() {
+ if (xPathValueTmpl.IsEmpty()) {
+ xPathValueTmpl = v8::ObjectTemplate::New();
+ xPathValueTmpl->SetInternalFieldCount(1);
+ xPathValueTmpl->Set(v8::String::New("asNodeSet"), v8::FunctionTemplate::New(jsXPathValueAsNodeSet));
+ }
+ return xPathValueTmpl;
+ }
+
+ v8::Handle<v8::ObjectTemplate> V8SCXMLDOM::nodeSetTmpl;
+ v8::Handle<v8::ObjectTemplate> V8SCXMLDOM::getNodeSetTmpl() {
+ if (nodeSetTmpl.IsEmpty()) {
+ nodeSetTmpl = v8::ObjectTemplate::New();
+ nodeSetTmpl->SetInternalFieldCount(1);
+ nodeSetTmpl->SetIndexedPropertyHandler(jsNodeSetGetIndex);
+ nodeSetTmpl->Set(v8::String::New("length"), v8::FunctionTemplate::New(jsNodeSetLength));
+ }
+ return nodeSetTmpl;
+ }
+
+ v8::Handle<v8::ObjectTemplate> V8SCXMLDOM::nodeTmpl;
+ v8::Handle<v8::ObjectTemplate> V8SCXMLDOM::getNodeTmpl() {
+ if (nodeTmpl.IsEmpty()) {
+ nodeTmpl = v8::ObjectTemplate::New();
+ nodeTmpl->SetInternalFieldCount(1);
+ nodeTmpl->Set(v8::String::New("appendChild"), v8::FunctionTemplate::New(jsNodeAppendChild));
+ }
+ return nodeTmpl;
+ }
+
+} \ No newline at end of file
diff --git a/src/uscxml/datamodel/ecmascript/v8/dom/V8SCXMLDOM.h b/src/uscxml/datamodel/ecmascript/v8/dom/V8SCXMLDOM.h
new file mode 100644
index 0000000..75920da
--- /dev/null
+++ b/src/uscxml/datamodel/ecmascript/v8/dom/V8SCXMLDOM.h
@@ -0,0 +1,58 @@
+#ifndef V8SCXMLDOM_H_AREM0ZC4
+#define V8SCXMLDOM_H_AREM0ZC4
+
+#include "uscxml/Interpreter.h"
+
+#include <DOM/Document.hpp>
+#include <v8.h>
+
+#include <list>
+
+namespace uscxml {
+
+class V8SCXMLDOM {
+public:
+ V8SCXMLDOM(Interpreter* interpreter);
+ virtual ~V8SCXMLDOM() {};
+
+ v8::Handle<v8::ObjectTemplate> getDocument();
+ static v8::Handle<v8::Value> jsDocumentCreateElement(const v8::Arguments& args);
+ static v8::Handle<v8::Value> jsDocumentEvaluate(const v8::Arguments& args);
+
+ static v8::Handle<v8::Value> jsElementTagName(v8::Local<v8::String> property, const v8::AccessorInfo &info);
+ static v8::Handle<v8::Value> jsElementGetAttribute(const v8::Arguments& args);
+ static v8::Handle<v8::Value> jsElementSetAttribute(const v8::Arguments& args);
+
+ static v8::Handle<v8::Value> jsXPathValueAsNodeSet(const v8::Arguments& args);
+
+ static v8::Handle<v8::Value> jsNodeSetGetIndex(uint32_t index, const v8::AccessorInfo &info);
+ static v8::Handle<v8::Value> jsNodeSetLength(const v8::Arguments& args);
+ static void jsNodeSetDestructor(v8::Persistent<v8::Value> object, void* data);
+
+
+ static v8::Handle<v8::Value> jsNodeAppendChild(const v8::Arguments& args);
+
+ static v8::Handle<v8::ObjectTemplate> getXPathValueTmpl();
+ static v8::Handle<v8::ObjectTemplate> getNodeSetTmpl();
+ static v8::Handle<v8::ObjectTemplate> getNodeTmpl();
+
+ Interpreter* _interpreter;
+ static v8::Handle<v8::ObjectTemplate> xPathValueTmpl;
+ static v8::Handle<v8::ObjectTemplate> nodeSetTmpl;
+ static v8::Handle<v8::ObjectTemplate> nodeTmpl;
+
+};
+
+class V8Node {
+};
+
+class V8DOMDocument {
+ V8DOMDocument();
+ virtual ~V8DOMDocument();
+
+ v8::Handle<v8::Array> jsChildNodes();
+};
+
+}
+
+#endif /* end of include guard: V8SCXMLDOM_H_AREM0ZC4 */
diff --git a/src/uscxml/debug/SCXMLDotWriter.cpp b/src/uscxml/debug/SCXMLDotWriter.cpp
new file mode 100644
index 0000000..cd947ce
--- /dev/null
+++ b/src/uscxml/debug/SCXMLDotWriter.cpp
@@ -0,0 +1,358 @@
+#include "SCXMLDotWriter.h"
+#include "uscxml/Interpreter.h"
+#include <boost/algorithm/string.hpp> // replace_all
+
+namespace uscxml {
+
+using namespace Arabica::DOM;
+
+int SCXMLDotWriter::_indentation = 0;
+
+SCXMLDotWriter::SCXMLDotWriter(Interpreter* interpreter) {
+ _interpreter = interpreter;
+}
+
+SCXMLDotWriter::~SCXMLDotWriter() {
+
+}
+
+std::string SCXMLDotWriter::getPrefix() {
+ std::string prefix = "";
+ for (int i = 0; i < _indentation; i++)
+ prefix += " ";
+ return prefix;
+}
+
+void SCXMLDotWriter::toDot(const std::string& filename, Interpreter* interpreter) {
+ std::ofstream outfile(filename.c_str());
+ NodeList<std::string > scxmlElems = interpreter->getDocument().getElementsByTagName("scxml");
+ SCXMLDotWriter writer(interpreter);
+ if (scxmlElems.getLength() > 0) {
+ _indentation++;
+ outfile << "digraph {" << std::endl;
+ outfile << "rankdir=LR;" << std::endl;
+ writer.writeSCXMLElement(outfile, (Arabica::DOM::Element<std::string>)scxmlElems.item(0));
+ _indentation--;
+ outfile << "}" << std::endl;
+ }
+
+}
+
+void SCXMLDotWriter::writeSCXMLElement(std::ostream& os, const Arabica::DOM::Element<std::string>& elem) {
+ writeStateElement(os, elem);
+
+// std::string elemId = idForNode(elem);
+// os << getPrefix() << "subgraph \"cluster" << elemId.substr(1, elemId.length() - 1) << " {" << std::endl;
+// _indentation++;
+// os << getPrefix() << "label=\"" << nameForNode(elem) << "\"" << std::endl;
+// writeStateElement(os, (Arabica::DOM::Element<std::string>)_interpreter->getInitialState());
+// os << getPrefix() << "} " << std::endl;
+
+}
+
+void SCXMLDotWriter::writeStateElement(std::ostream& os, const Arabica::DOM::Element<std::string>& elem) {
+
+ std::string elemId = idForNode(elem);
+ NodeList<std::string > childElems = elem.getChildNodes();
+
+ if (_knownIds.find(elemId) != _knownIds.end())
+ return;
+ _knownIds.insert(elemId);
+
+ bool subgraph = Interpreter::isCompound(elem) || Interpreter::isParallel(elem);
+ if (subgraph) {
+ _indentation++;
+ os << getPrefix() << "subgraph \"cluster_" << elemId << "\" {" << std::endl;
+ os << getPrefix() << "label=\"" << nameForNode(elem) << "\\l\"" << std::endl;
+ }
+
+ os << getPrefix() << "\"" << elemId << "\"[";
+ os << "label=<<b>State</b><br />" << nameForNode(elem) << ">,";
+ if (_interpreter->isInitial(elem))
+ os << "style=filled,";
+ if (_interpreter->isFinal(elem))
+ os << "shape=doublecircle,";
+ os << "];" << std::endl;
+
+ std::string details = getDetailedLabel(elem);
+// std::cout << details << std::endl;
+
+ if (details.size() > 0) {
+ os << getPrefix() << "\"" << elemId << "Exec\"[";
+// os << "fontsize=10,";
+ os << "shape=box,";
+ os << "color=grey,";
+ os << "label=<" << details << ">";
+ os << "]" << std::endl;
+ os << getPrefix() << "\"" << elemId << "\" -> \"" << elemId << "Exec\" [arrowhead=none, color=grey]" << std::endl;
+ }
+
+// NodeList<std::string > childElems = elem.getChildNodes();
+// for (int i = 0; i < childElems.getLength(); i++) {
+// if (Interpreter::isState(childElems.item(i))) {
+// writeStateElement(os, (Arabica::DOM::Element<std::string>)childElems.item(i));
+// }
+// }
+
+ for (int i = 0; i < childElems.getLength(); i++) {
+ if (childElems.item(i).getNodeType() == Node_base::ELEMENT_NODE && boost::iequals(TAGNAME(childElems.item(i)), "transition")) {
+ writeTransitionElement(os, (Arabica::DOM::Element<std::string>)childElems.item(i));
+ os << getPrefix() << "\"" << elemId << "\" -> \"" << idForNode(childElems.item(i)) << "\"" << std::endl;
+ }
+ if (Interpreter::isState(childElems.item(i))) {
+ writeStateElement(os, (Arabica::DOM::Element<std::string>)childElems.item(i));
+ }
+ if (childElems.item(i).getNodeType() == Node_base::ELEMENT_NODE && boost::iequals(TAGNAME(childElems.item(i)), "initial")) {
+ NodeList<std::string > grandChildElems = childElems.item(i).getChildNodes();
+ for (int j = 0; j < grandChildElems.getLength(); j++) {
+ if (grandChildElems.item(j).getNodeType() == Node_base::ELEMENT_NODE && boost::iequals(TAGNAME(grandChildElems.item(j)), "transition")) {
+ writeTransitionElement(os, (Arabica::DOM::Element<std::string>)grandChildElems.item(j));
+ os << getPrefix() << "\"" << elemId << "\" -> \"" << idForNode(grandChildElems.item(j)) << "\"" << std::endl;
+ }
+ }
+ }
+ }
+
+ if (subgraph) {
+ _indentation--;
+ os << getPrefix() << "} " << std::endl;
+ }
+
+}
+
+void SCXMLDotWriter::writeTransitionElement(std::ostream& os, const Arabica::DOM::Element<std::string>& elem) {
+ std::string elemId = idForNode(elem);
+
+ Arabica::XPath::NodeSet<std::string> targetStates = _interpreter->getTargetStates(elem);
+
+ std::string label;
+ os << getPrefix() << "\"" << elemId << "\"[";
+// os << "fontsize=10,";
+ os << "shape=box,";
+ os << "label=<<b>Transition</b><br align=\"left\" />";
+ if (HAS_ATTR(elem, "event"))
+ os << "event: " << ATTR(elem, "event");
+ if (HAS_ATTR(elem, "cond"))
+ os << "cond: " << ATTR(elem, "cond");
+ if (!HAS_ATTR(elem, "cond") && !HAS_ATTR(elem, "event"))
+ os << "unconditional";
+ os << ">";
+ os << "]" << std::endl;
+
+ for (int i = 0; i < targetStates.size(); i++) {
+ os << getPrefix() << "\"" << elemId << "\" -> \"" << idForNode(targetStates[i]) << "\"" << std::endl;
+ writeStateElement(os, (Arabica::DOM::Element<std::string>)targetStates[i]);
+ }
+
+}
+
+std::string SCXMLDotWriter::getDetailedLabel(const Arabica::DOM::Element<std::string>& elem, int indentation) {
+
+/*
+ <table>
+ <tr>
+ <td colspan="2">onEntry</td>
+ </tr>
+ <tr>
+ <td>Details</td>
+ <td bgcolor="#eee">
+ Nested Content
+ </td>
+ </tr>
+ </table>
+*/
+
+ std::list<struct ElemDetails> content;
+
+ NodeList<std::string > childElems = elem.getChildNodes();
+ for (int i = 0; i < childElems.getLength(); i++) {
+ if (childElems.item(i).getNodeType() != Node_base::ELEMENT_NODE)
+ continue;
+
+ if (Interpreter::isState(childElems.item(i)) ||
+ boost::iequals(TAGNAME(childElems.item(i)), "transition") ||
+ boost::iequals(TAGNAME(childElems.item(i)), "initial") ||
+ false)
+ continue;
+
+ struct ElemDetails details;
+ details.name = "<b>" + TAGNAME(childElems.item(i)) + "</b>";
+
+ // provide details for special elements here
+
+ // param ---------
+ if (boost::iequals(TAGNAME(childElems.item(i)), "param")) {
+ if (HAS_ATTR(childElems.item(i), "name"))
+ details.name += " " + ATTR(childElems.item(i), "name") + " = ";
+ if (HAS_ATTR(childElems.item(i), "expr"))
+ details.name += ATTR(childElems.item(i), "expr");
+ if (HAS_ATTR(childElems.item(i), "location"))
+ details.name += ATTR(childElems.item(i), "location");
+ }
+
+ // data ---------
+ if (boost::iequals(TAGNAME(childElems.item(i)), "data")) {
+ if (HAS_ATTR(childElems.item(i), "id"))
+ details.name += " " + ATTR(childElems.item(i), "id") + " = ";
+ if (HAS_ATTR(childElems.item(i), "src"))
+ details.name += ATTR(childElems.item(i), "src");
+ if (HAS_ATTR(childElems.item(i), "expr"))
+ details.name += ATTR(childElems.item(i), "expr");
+ NodeList<std::string > grandChildElems = childElems.item(i).getChildNodes();
+ for (int j = 0; j < grandChildElems.getLength(); j++) {
+ if (grandChildElems.item(j).getNodeType() == Node_base::TEXT_NODE) {
+ details.name += dotEscape(grandChildElems.item(j).getNodeValue());
+ }
+ }
+ }
+
+ // invoke ---------
+ if (boost::iequals(TAGNAME(childElems.item(i)), "invoke")) {
+ if (HAS_ATTR(childElems.item(i), "type"))
+ details.name += "<br />type = " + ATTR(childElems.item(i), "type");
+ if (HAS_ATTR(childElems.item(i), "typeexpr"))
+ details.name += "<br />type = " + ATTR(childElems.item(i), "typeexpr");
+ if (HAS_ATTR(childElems.item(i), "src"))
+ details.name += "<br />src = " + ATTR(childElems.item(i), "src");
+ if (HAS_ATTR(childElems.item(i), "srcexpr"))
+ details.name += "<br />src = " + ATTR(childElems.item(i), "srcexpr");
+ if (HAS_ATTR(childElems.item(i), "id"))
+ details.name += "<br />id = " + ATTR(childElems.item(i), "id");
+ if (HAS_ATTR(childElems.item(i), "idlocation"))
+ details.name += "<br />id = " + ATTR(childElems.item(i), "idlocation");
+ }
+
+ // send ---------
+ if (boost::iequals(TAGNAME(childElems.item(i)), "send")) {
+ if (HAS_ATTR(childElems.item(i), "type"))
+ details.name += "<br />type = " + ATTR(childElems.item(i), "type");
+ if (HAS_ATTR(childElems.item(i), "typeexpr"))
+ details.name += "<br />type = " + ATTR(childElems.item(i), "typeexpr");
+ if (HAS_ATTR(childElems.item(i), "event"))
+ details.name += "<br />event = " + ATTR(childElems.item(i), "event");
+ if (HAS_ATTR(childElems.item(i), "eventexpr"))
+ details.name += "<br />event = " + ATTR(childElems.item(i), "eventexpr");
+ if (HAS_ATTR(childElems.item(i), "target"))
+ details.name += "<br />target = " + ATTR(childElems.item(i), "target");
+ if (HAS_ATTR(childElems.item(i), "targetexpr"))
+ details.name += "<br />target = " + ATTR(childElems.item(i), "targetexpr");
+ if (HAS_ATTR(childElems.item(i), "delay"))
+ details.name += "<br />delay = " + ATTR(childElems.item(i), "delay");
+ if (HAS_ATTR(childElems.item(i), "delayexpr"))
+ details.name += "<br />delay = " + ATTR(childElems.item(i), "delayexpr");
+ }
+
+ // script ---------
+ if (boost::iequals(TAGNAME(childElems.item(i)), "script")) {
+ details.name += " ";
+ if (HAS_ATTR(childElems.item(i), "src"))
+ details.name += ATTR(childElems.item(i), "src");
+ NodeList<std::string > grandChildElems = childElems.item(i).getChildNodes();
+ for (int j = 0; j < grandChildElems.getLength(); j++) {
+ if (grandChildElems.item(j).getNodeType() == Node_base::TEXT_NODE) {
+ details.name += dotEscape(grandChildElems.item(j).getNodeValue());
+ }
+ }
+ }
+
+ // recurse
+ details.content = getDetailedLabel((Arabica::DOM::Element<std::string>)childElems.item(i), indentation + 1);
+ content.push_back(details);
+ }
+
+ std::stringstream ssContent;
+
+ if (content.size() > 0) {
+ ssContent << "<table cellspacing=\"2\" cellpadding=\"0\" border=\"0\">";
+
+ std::list<struct ElemDetails>::iterator contentIter = content.begin();
+ while(contentIter != content.end()) {
+ ssContent << "<tr>";
+// ssContent << "<td align=\"left\" colspan=\"2\">" << contentIter->name << "</td>";
+ ssContent << "<td balign=\"left\" align=\"left\">" << contentIter->name << "</td>";
+ ssContent << "</tr>";
+
+ if (contentIter->content.size() > 0) {
+ ssContent << "<tr>";
+// ssContent << "<td>" << contentIter->details << "</td>";
+ ssContent << "<td bgcolor=\"#" << colorForIndent(indentation + 1) << "\">" << contentIter->content << "</td>";
+ ssContent << "</tr>";
+ }
+ contentIter++;
+
+ }
+ ssContent << "</table>";
+ }
+ return ssContent.str();
+}
+
+std::string SCXMLDotWriter::dotEscape(const std::string& text) {
+ std::string escaped(text);
+ boost::replace_all(escaped, "", "");
+
+ return escaped;
+}
+
+std::string SCXMLDotWriter::colorForIndent(int indent) {
+ int color = 255 - (16 * indent);
+ std::stringstream ss;
+ ss << std::hex << color;
+ ss << std::hex << color;
+ ss << std::hex << color;
+ return ss.str();
+}
+
+std::string SCXMLDotWriter::nameForNode(const Arabica::DOM::Node<std::string>& node) {
+ std::string elemName;
+ if (node.getNodeType() == Node_base::ELEMENT_NODE) {
+ Arabica::DOM::Element<std::string> elem = (Arabica::DOM::Element<std::string>)node;
+ if (elem.hasAttribute("name")) {
+ elemName = elem.getAttribute("name");
+ } else if (elem.hasAttribute("id")) {
+ elemName = elem.getAttribute("id");
+ }
+ }
+ if (elemName.size() == 0)
+ elemName = boost::lexical_cast<std::string>(node.getLocalName());
+
+ return elemName;
+
+}
+
+std::string SCXMLDotWriter::idForNode(const Arabica::DOM::Node<std::string>& node) {
+ std::string elemId;
+ if (node.getNodeType() == Node_base::ELEMENT_NODE) {
+ Arabica::DOM::Element<std::string> elem = (Arabica::DOM::Element<std::string>)node;
+ if (elem.hasAttribute("name")) {
+ elemId = elem.getAttribute("name");
+ } else if (elem.hasAttribute("id")) {
+ elemId = elem.getAttribute("id");
+ }
+ }
+ if (elemId.size() == 0) {
+ Arabica::DOM::Node<std::string> tmpParent = node;
+ Arabica::DOM::Node<std::string> tmpIndex;
+ do {
+ if (tmpParent.getNodeType() != Node_base::ELEMENT_NODE)
+ continue;
+
+ tmpIndex = tmpParent;
+ int index = 0;
+
+ while((tmpIndex = tmpIndex.getPreviousSibling()))
+ index++;
+
+ std::stringstream ssElemId;
+ ssElemId << TAGNAME(tmpParent) << index << ".";
+ elemId = ssElemId.str() + elemId;
+ } while ((tmpParent = tmpParent.getParentNode()));
+// elemId = ssElemId.str();
+ }
+
+ std::replace(elemId.begin(), elemId.end(), '-', '_');
+// std::replace(elemId.begin(), elemId.end(), '.', '_');
+
+ return elemId;
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/debug/SCXMLDotWriter.h b/src/uscxml/debug/SCXMLDotWriter.h
new file mode 100644
index 0000000..7ebb916
--- /dev/null
+++ b/src/uscxml/debug/SCXMLDotWriter.h
@@ -0,0 +1,45 @@
+#ifndef SCXMLDOTWRITER_H_AOP0OHXX
+#define SCXMLDOTWRITER_H_AOP0OHXX
+
+#include <DOM/Document.hpp>
+#include <fstream>
+#include <set>
+
+namespace uscxml {
+
+class Interpreter;
+
+class SCXMLDotWriter {
+public:
+
+ struct ElemDetails {
+ std::string name;
+ std::string details;
+ std::string content;
+ };
+
+ SCXMLDotWriter(Interpreter* interpreter);
+ ~SCXMLDotWriter();
+
+ static void toDot(const std::string& filename, Interpreter* interpreter);
+ void writeSCXMLElement(std::ostream& os, const Arabica::DOM::Element<std::string>& elem);
+ void writeStateElement(std::ostream& os, const Arabica::DOM::Element<std::string>& elem);
+ void writeTransitionElement(std::ostream& os, const Arabica::DOM::Element<std::string>& elem);
+
+ std::string getDetailedLabel(const Arabica::DOM::Element<std::string>& elem, int indentation = 0);
+ std::string colorForIndent(int indent);
+
+ std::string idForNode(const Arabica::DOM::Node<std::string>& node);
+ std::string nameForNode(const Arabica::DOM::Node<std::string>& node);
+
+ static std::string getPrefix();
+ static std::string dotEscape(const std::string& text);
+
+ Interpreter* _interpreter;
+ std::set<std::string> _knownIds;
+ static int _indentation;
+};
+
+}
+
+#endif /* end of include guard: SCXMLDOTWRITER_H_AOP0OHXX */
diff --git a/src/uscxml/invoker/modality/MMIComponent.cpp b/src/uscxml/invoker/modality/MMIComponent.cpp
new file mode 100644
index 0000000..22c2e17
--- /dev/null
+++ b/src/uscxml/invoker/modality/MMIComponent.cpp
@@ -0,0 +1,42 @@
+#include "MMIComponent.h"
+#include "uscxml/Interpreter.h"
+
+namespace uscxml {
+
+MMIComponent::MMIComponent() {
+}
+
+
+MMIComponent::~MMIComponent() {
+};
+
+Invoker* MMIComponent::create(Interpreter* interpreter) {
+ MMIComponent* invoker = new MMIComponent();
+ invoker->_interpreter = interpreter;
+ return invoker;
+}
+
+Data MMIComponent::getDataModelVariables() {
+ Data data;
+ return data;
+}
+
+void MMIComponent::send(SendRequest& req) {
+ assert(false);
+}
+
+void MMIComponent::cancel(const std::string sendId) {
+ assert(false);
+}
+
+void MMIComponent::sendToParent(SendRequest& req) {
+ req.invokeid = _invokeId;
+ assert(false);
+}
+
+void MMIComponent::invoke(InvokeRequest& req) {
+ _invokeId = req.invokeid;
+
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/invoker/modality/MMIComponent.h b/src/uscxml/invoker/modality/MMIComponent.h
new file mode 100644
index 0000000..a83775f
--- /dev/null
+++ b/src/uscxml/invoker/modality/MMIComponent.h
@@ -0,0 +1,109 @@
+#ifndef MMICOMPONENT_H_MZ1I550N
+#define MMICOMPONENT_H_MZ1I550N
+
+#include "uscxml/Factory.h"
+
+namespace uscxml {
+
+class Interpreter;
+
+class MMIComponent : public Invoker {
+public:
+
+ enum State {
+ PAUSED,
+ RUNNING,
+ IDLE,
+ TERMINATED
+ };
+
+ MMIComponent();
+ virtual ~MMIComponent();
+ virtual Invoker* create(Interpreter* interpreter);
+
+ virtual Data getDataModelVariables();
+ virtual void send(SendRequest& req);
+ virtual void cancel(const std::string sendId);
+ virtual void invoke(InvokeRequest& req);
+ virtual void sendToParent(SendRequest& req);
+
+protected:
+ std::string _invokeId;
+ Interpreter* _interpreter;
+
+ State _state;
+};
+
+
+/** Base classes for MMI messages */
+
+class MMICoreMessage {
+public:
+ std::string source;
+ std::string target;
+ std::string data;
+ std::string requestId;
+};
+
+class MMICtxMessage : public MMICoreMessage {
+public:
+ std::string context;
+};
+
+class MMIStartMessage : public MMICtxMessage {
+public:
+ std::string content;
+ std::string contentURL;
+};
+
+class MMISimpleStatusMessage : public MMICtxMessage {
+public:
+ std::string status;
+};
+
+class MMIStatusMessage : public MMISimpleStatusMessage {
+public:
+ std::string statusInfo;
+};
+
+/** Concrete MMI messages */
+
+class MMINewContextRequest : public MMICoreMessage {};
+
+/***/
+
+class MMIPauseRequest : public MMICtxMessage {};
+class MMIResumeRequest : public MMICtxMessage {};
+class MMICancelRequest : public MMICtxMessage {};
+class MMIClearContextRequest : public MMICtxMessage {};
+class MMIStatusRequest : public MMICtxMessage {};
+
+/***/
+
+class MMIStartRequest : public MMIStartMessage {};
+class MMIPrepareRequest : public MMIStartMessage {};
+
+/***/
+
+class MMIExtensionNotification : public MMICtxMessage {
+ std::string name;
+};
+
+/***/
+
+class MMIStatusResponse : public MMISimpleStatusMessage {};
+
+/***/
+
+class MMIStartResponse : public MMIStatusMessage {};
+class MMIPrepareRespnse : public MMIStatusMessage {};
+class MMIPauseResponse : public MMIStatusMessage {};
+class MMIResumeResponse : public MMIStatusMessage {};
+class MMICancelResponse : public MMIStatusMessage {};
+class MMIDoneNotification : public MMIStatusMessage {};
+class MMINewContextResponse : public MMIStatusMessage {};
+class MMIClearContextResponse : public MMIStatusMessage {};
+
+}
+
+#endif /* end of include guard: MMICOMPONENT_H_MZ1I550N */
diff --git a/src/uscxml/invoker/modality/UmundoComponent.cpp b/src/uscxml/invoker/modality/UmundoComponent.cpp
new file mode 100644
index 0000000..22dd279
--- /dev/null
+++ b/src/uscxml/invoker/modality/UmundoComponent.cpp
@@ -0,0 +1,49 @@
+#include "UmundoComponent.h"
+#include "uscxml/Interpreter.h"
+
+namespace uscxml {
+
+UmundoComponent::UmundoComponent() {
+}
+
+
+UmundoComponent::~UmundoComponent() {
+ delete _invokedInterpreter;
+};
+
+Invoker* UmundoComponent::create(Interpreter* interpreter) {
+ UmundoComponent* invoker = new UmundoComponent();
+ invoker->_parentInterpreter = interpreter;
+ return invoker;
+}
+
+Data UmundoComponent::getDataModelVariables() {
+ Data data;
+ return data;
+}
+
+void UmundoComponent::send(SendRequest& req) {
+ assert(false);
+}
+
+void UmundoComponent::cancel(const std::string sendId) {
+ assert(false);
+}
+
+void UmundoComponent::sendToParent(SendRequest& req) {
+ req.invokeid = _invokeId;
+ _parentInterpreter->receive(req);
+}
+
+void UmundoComponent::invoke(InvokeRequest& req) {
+ _invokeId = req.invokeid;
+ _invokedInterpreter = Interpreter::fromURI(req.src);
+ DataModel* dataModel = _invokedInterpreter->getDataModel();
+ if (dataModel != NULL) {
+
+ }
+ _invokedInterpreter->setInvoker(this);
+ _invokedInterpreter->start();
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/invoker/modality/UmundoComponent.h b/src/uscxml/invoker/modality/UmundoComponent.h
new file mode 100644
index 0000000..f2c76c4
--- /dev/null
+++ b/src/uscxml/invoker/modality/UmundoComponent.h
@@ -0,0 +1,30 @@
+#ifndef UMUNDOCOMPONENT_H_VMW54W1R
+#define UMUNDOCOMPONENT_H_VMW54W1R
+
+#include "MMIComponent.h"
+
+namespace uscxml {
+
+class Interpreter;
+
+class UmundoComponent : public MMIComponent {
+public:
+ UmundoComponent();
+ virtual ~UmundoComponent();
+ virtual Invoker* create(Interpreter* interpreter);
+
+ virtual Data getDataModelVariables();
+ virtual void send(SendRequest& req);
+ virtual void cancel(const std::string sendId);
+ virtual void invoke(InvokeRequest& req);
+ virtual void sendToParent(SendRequest& req);
+
+protected:
+ std::string _invokeId;
+ Interpreter* _invokedInterpreter;
+ Interpreter* _parentInterpreter;
+};
+
+}
+
+#endif /* end of include guard: UMUNDOCOMPONENT_H_VMW54W1R */
diff --git a/src/uscxml/invoker/modality/miles/SpatialAudio.cpp b/src/uscxml/invoker/modality/miles/SpatialAudio.cpp
new file mode 100644
index 0000000..559bcaa
--- /dev/null
+++ b/src/uscxml/invoker/modality/miles/SpatialAudio.cpp
@@ -0,0 +1,153 @@
+#include "SpatialAudio.h"
+#include "uscxml/Interpreter.h"
+
+#include <glog/logging.h>
+
+#include <math.h>
+
+namespace uscxml {
+
+SpatialAudio::SpatialAudio() {
+ _audioDevOpen = false;
+ _audioDev = NULL;
+ _audioDevIndex = -1;
+ _pos = new float[3];
+ _pos[0] = _pos[1] = _pos[2] = 0.0;
+}
+
+
+SpatialAudio::~SpatialAudio() {
+};
+
+Invoker* SpatialAudio::create(Interpreter* interpreter) {
+ SpatialAudio* invoker = new SpatialAudio();
+ invoker->_interpreter = interpreter;
+ return invoker;
+}
+
+Data SpatialAudio::getDataModelVariables() {
+ Data data;
+ return data;
+}
+
+void SpatialAudio::send(SendRequest& req) {
+ setPosFromParams(req.params);
+ if (boost::iequals(req.name, "play")) {
+ if (!_audioDevOpen) {
+ _audioDev = miles_audio_device_open(_audioDevIndex, 0, 22050, 2, 1, 0);
+ }
+ if (_audioDev != NULL) {
+ _audioDevOpen = true;
+ miles_audio_device_control(_audioDev, MILES_AUDIO_DEVICE_CTRL_SET_POSITION, _pos);
+
+ char* buffer = (char*)malloc(_audioDev->chunk_size);
+ // skip wav header
+ url_fread(buffer, 44, 1, _urlHandle);
+ int read = 0;
+ while((read = url_fread(buffer, _audioDev->chunk_size, 1, _urlHandle) != 0)) {
+ miles_audio_device_write(_audioDev, buffer, _audioDev->chunk_size);
+ }
+ url_rewind(_urlHandle);
+ free(buffer);
+ }
+ }
+}
+
+void SpatialAudio::cancel(const std::string sendId) {
+ assert(false);
+}
+
+void SpatialAudio::sendToParent(SendRequest& req) {
+ req.invokeid = _invokeId;
+ assert(false);
+}
+
+void SpatialAudio::invoke(InvokeRequest& req) {
+ _invokeId = req.invokeid;
+
+ if (req.src.length() > 0) {
+ _urlHandle = url_fopen(req.src.c_str(), "r");
+ if(!_urlHandle) {
+ LOG(ERROR) << "couldn't url_fopen() " << req.src;
+ }
+ }
+
+ setPosFromParams(req.params);
+
+ struct miles_audio_device_description *devices;
+ int ndevs;
+
+ ndevs = miles_audio_device_get_supported_devices(&devices);
+
+ for (int i = 0; i < ndevs; i++) {
+ if ((devices[i].capabilities & MILES_AUDIO_DEVICE_CAPABILITY_SPATIAL) &&
+ (devices[i].capabilities & MILES_AUDIO_DEVICE_CAPABILITY_OUTPUT)) {
+ _audioDevIndex = i;
+ break;
+ }
+ }
+}
+
+void SpatialAudio::setPosFromParams(std::map<std::string, std::string>& params) {
+ // vector explicitly given
+ try {
+ if (params.find("x") != params.end())
+ _pos[0] = boost::lexical_cast<float>(params["x"]);
+ if (params.find("y") != params.end())
+ _pos[1] = boost::lexical_cast<float>(params["y"]);
+ if (params.find("z") != params.end())
+ _pos[2] = boost::lexical_cast<float>(params["z"]);
+ } catch (boost::bad_lexical_cast& e) {
+ LOG(ERROR) << "Cannot interpret x, y or z as float value in params: " << e.what();
+ }
+
+ try {
+ // right is an alias for x
+ if (params.find("right") != params.end())
+ _pos[0] = boost::lexical_cast<float>(params["right"]);
+ // height is an alias for y
+ if (params.find("height") != params.end())
+ _pos[1] = boost::lexical_cast<float>(params["height"]);
+ // front is an alias for z
+ if (params.find("front") != params.end())
+ _pos[2] = boost::lexical_cast<float>(params["front"]);
+ } catch (boost::bad_lexical_cast& e) {
+ LOG(ERROR) << "Cannot interpret right, height or front as float value in params: " << e.what();
+ }
+
+ // do we have a position on a circle?
+ try {
+ if (params.find("circle") != params.end()) {
+ float rad = posToRadian(params["circle"]);
+ _pos[0] = cosf(rad);
+ _pos[2] = -1 * sinf(rad); // z axis increases to front
+ }
+ } catch (boost::bad_lexical_cast& e) {
+ LOG(ERROR) << "Cannot interpret circle as float value in params: " << e.what();
+ }
+// std::cout << _pos[0] << ":" << _pos[1] << ":" << _pos[2] << std::endl;
+
+}
+
+float SpatialAudio::posToRadian(std::string& position) {
+ boost::trim(position);
+ float rad = 0;
+
+ if (position.size() > 3 && boost::iequals("deg", position.substr(position.length() - 3, 3))) {
+ rad = boost::lexical_cast<float>(position.substr(0, position.size() - 3));
+ rad = fmodf(rad, 360); // into range [0-360]
+ rad /= 180; // into range [0-2]
+ rad *= M_PI; // into range [0-2PI]
+ rad -= M_PI_2; // 0 to top;
+ rad *= -1; // make clockwise
+ rad += 2 * M_PI; // make positive
+ } else if (position.size() > 3 && boost::iequals("rad", position.substr(position.length() - 3, 3))) {
+ rad = boost::lexical_cast<float>(position.substr(0, position.size() - 3));
+ rad = fmodf(rad, M_PI * 2); // into range [0-2*PI]
+ } else {
+ LOG(ERROR) << "Cannot make sense of position value " << position << ": does not end in 'deg', 'rad'";
+ }
+ return rad;
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/invoker/modality/miles/SpatialAudio.h b/src/uscxml/invoker/modality/miles/SpatialAudio.h
new file mode 100644
index 0000000..926c82a
--- /dev/null
+++ b/src/uscxml/invoker/modality/miles/SpatialAudio.h
@@ -0,0 +1,49 @@
+#ifndef SPATIALAUDIO_H_EH11SAQC
+#define SPATIALAUDIO_H_EH11SAQC
+
+#include <map>
+
+#include "uscxml/Utilities.h"
+#include "../MMIComponent.h"
+
+extern "C" {
+# include "miles/audio.h"
+# include "miles/audio_codec.h"
+# include "miles/audio_device.h"
+}
+
+namespace uscxml {
+
+class Interpreter;
+
+class SpatialAudio : public MMIComponent {
+public:
+ SpatialAudio();
+ virtual ~SpatialAudio();
+ virtual Invoker* create(Interpreter* interpreter);
+
+ virtual Data getDataModelVariables();
+ virtual void send(SendRequest& req);
+ virtual void cancel(const std::string sendId);
+ virtual void invoke(InvokeRequest& req);
+ virtual void sendToParent(SendRequest& req);
+
+ void setPosFromParams(std::map<std::string, std::string>& params);
+ static float posToRadian(std::string& position);
+
+protected:
+ std::string _invokeId;
+ Interpreter* _invokedInterpreter;
+
+ URL_FILE* _urlHandle;
+
+ float* _pos;
+ bool _audioDevOpen;
+ int _audioDevIndex;
+ struct miles_audio_device* _audioDev;
+
+};
+
+}
+
+#endif /* end of include guard: SPATIALAUDIO_H_EH11SAQC */
diff --git a/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp b/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp
index d0adcb3..13052c7 100644
--- a/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp
+++ b/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp
@@ -69,7 +69,7 @@ void EventIOProcessor::send(SendRequest& req) {
const char* hostName = evhttp_uri_get_host(targetURI);
// use synchronous dns resolving for multicast dns
- if(strlen(hostName) >= strlen(".local")) {
+ if(hostName && strlen(hostName) >= strlen(".local")) {
if(strcmp(hostName + strlen(hostName) - strlen(".local"), ".local") == 0) {
evhttp_uri_set_host(targetURI, EventIOServer::syncResolve(hostName).c_str());
}
diff --git a/test/src/audio/click.wav b/test/src/audio/click.wav
new file mode 100644
index 0000000..e11b0b7
--- /dev/null
+++ b/test/src/audio/click.wav
Binary files differ
diff --git a/test/src/scxml-gui-test.scxml b/test/src/scxml-gui-test.scxml
new file mode 100644
index 0000000..adf93b8
--- /dev/null
+++ b/test/src/scxml-gui-test.scxml
@@ -0,0 +1,13 @@
+<scxml initial="start" version="0.9" xmlns="http://www.w3.org/2005/07/scxml"><!-- node-size-and-position x=0.0 y=0.0 w=280.0 h=320.0 -->
+ <state id="start"><!-- node-size-and-position x=30.000000000000004 y=30.000000000000004 w=80.0 h=70.0 -->
+ <onentry>
+ <log expr="Starting"></log>
+ </onentry>
+ <transition target="final"></transition>
+ </state>
+ <final id="final"><!-- node-size-and-position x=160.0 y=30.000000000000004 w=100.0 h=100.0 -->
+ <onentry>
+ <log expr="final entered"></log>
+ </onentry>
+ </final>
+</scxml> \ No newline at end of file
diff --git a/test/src/test-completion.cpp b/test/src/test-completion.cpp
index 33056f3..619bdef 100644
--- a/test/src/test-completion.cpp
+++ b/test/src/test-completion.cpp
@@ -1,4 +1,5 @@
#include "uscxml/Interpreter.h"
+#include "uscxml/debug/SCXMLDotWriter.h"
#include <DOM/io/Stream.hpp>
int main(int argc, char** argv) {
@@ -10,7 +11,10 @@ int main(int argc, char** argv) {
using namespace uscxml;
Interpreter* interpreter = Interpreter::fromURI(argv[1]);
+ SCXMLDotWriter::toDot("output.dot", interpreter);
+
interpreter->interpret();
+
return EXIT_SUCCESS;
} \ No newline at end of file
diff --git a/test/src/test-dom.scxml b/test/src/test-dom.scxml
new file mode 100644
index 0000000..4624209
--- /dev/null
+++ b/test/src/test-dom.scxml
@@ -0,0 +1,20 @@
+<scxml datamodel="ecmascript">
+ <state id="start">
+ <onentry>
+ <script>
+ <!-- Add the missing transition to final -->
+ var transition = document.createElement("transition");
+ transition.setAttribute("target", "final");
+ transition.setAttribute("event", "quit");
+ var nodeSet = document.evaluate("//state[@id='start']").asNodeSet();
+ nodeSet[0].appendChild(transition);
+ </script>
+ <raise event="quit" />
+ </onentry>
+ </state>
+ <final id="final">
+ <onentry>
+ <log expr="'Quitting!'" />
+ </onentry>
+ </final>
+</scxml> \ No newline at end of file
diff --git a/test/src/test-spatial-audio.scxml b/test/src/test-spatial-audio.scxml
new file mode 100644
index 0000000..f800c82
--- /dev/null
+++ b/test/src/test-spatial-audio.scxml
@@ -0,0 +1,68 @@
+<scxml datamodel="ecmascript" name="spatial.audio.test">
+ <datamodel>
+ <!-- Alternative to the JSON notation
+ <data id="spatialAudio" expr="{}" />
+ <data id="spatialAudio.id2" expr="{}" />
+ <data id="spatialAudio.id2.degree" expr="90" />
+ -->
+ <data id="spatialAudio">
+ {
+ 'id2': {
+ 'degree': 90
+ }
+ }
+ </data>
+ </datamodel>
+ <state id="click.continuously">
+ <initial>
+ <transition target="ready" />
+ </initial>
+ <invoke type="http://www.smartvortex.eu/mmi/spatial-audio/" src="audio/click.wav" id="spatial-audio.id1">
+ <!--
+ Explicitly set the start position of an audio source.
+ Keep in mind that the coordinate system is the same as in OpenGL:
+ y
+ |
+ |___x
+ /
+ z
+ -->
+ <param name="x" expr="-1.0" /> <!-- leftmost -->
+ <param name="y" expr="0" />
+ <param name="z" expr="0" />
+ </invoke>
+ <invoke type="http://www.smartvortex.eu/mmi/spatial-audio/" src="audio/click.wav" id="spatial-audio.id2">
+ <!--
+ Set position on a circle.
+ Unit for circle is "deg" in clockwise order with 0 being directly in front,
+ or "rad" in counter-clockwise with zero being the rightmost position.
+ "right", "height" and "front" are aliases for x, y and z respectively.
+ -->
+ <param name="circle" expr="spatialAudio.id2.degree + 'deg'" /> <!-- rightmost -->
+ <param name="height" expr="0.0" />
+ </invoke>
+ <state id="ready">
+ <onentry>
+ <!-- trigger transition in 1 second -->
+ <send event="spatial-audio.play" delay="1s" />
+ </onentry>
+ <transition target="click" event="spatial-audio.play" />
+ <transition target="final" event="quit" />
+ </state>
+ <state id="click">
+ <onentry>
+ <send target="#_spatial-audio.id1" event="play" delay="0ms" />
+ <!--
+ move position of audio 2 in a circle
+ -->
+ <send target="#_spatial-audio.id2" event="play" delay="200ms">
+ <param name="circle" expr="spatialAudio.id2.degree + 'deg'" />
+ </send>
+ <script>spatialAudio.id2.degree += 10;</script>
+ </onentry>
+ <transition target="ready" />
+ </state>
+ </state>
+
+ <final id="final" />
+</scxml> \ No newline at end of file