diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2012-09-08 23:26:39 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2012-09-08 23:26:39 (GMT) |
commit | aa6c3a1257a29cc5bcf8b94893732ee553f27582 (patch) | |
tree | 625adb1a353a17ffed2c7e0bac686d705aaec93f | |
download | uscxml-aa6c3a1257a29cc5bcf8b94893732ee553f27582.zip uscxml-aa6c3a1257a29cc5bcf8b94893732ee553f27582.tar.gz uscxml-aa6c3a1257a29cc5bcf8b94893732ee553f27582.tar.bz2 |
Initial upload
Still somewhat quirky to build but fairly feature complete
142 files changed, 11445 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f77e4ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +*.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c462751 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,109 @@ +cmake_minimum_required(VERSION 2.8.4) + +project(uscxml) + +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/contrib/cmake) + +set(USCXML_LIBS) +set(USCXML_FILES) + +# libxml2 +find_package(LibXml2 REQUIRED) +include_directories(${LIBXML2_INCLUDE_DIR}) +list (APPEND USCXML_LIBS ${LIBXML2_LIBRARIES}) + +# prefer rest as static libraries +set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) + +#glog +set(ENV{GLOG_SRC} ${CMAKE_SOURCE_DIR}/../glog) +find_package(GLOG REQUIRED) +include_directories(${GLOG_INCLUDE_DIR}) +list (APPEND USCXML_LIBS ${GLOG_LIBRARY}) + +# arabica +find_package(Arabica REQUIRED) +include_directories(${ARABICA_INCLUDE_DIR}) +list (APPEND USCXML_LIBS ${ARABICA_LIBRARY}) + +# boost - header only +FIND_PATH(Boost_INCLUDE_DIR boost/version.hpp PATHS /usr/include) +include_directories(${Boost_INCLUDE_DIR}) + +#event +set(ENV{EVENT_SRC} ${CMAKE_SOURCE_DIR}/../libevent) +find_package(EVENT REQUIRED) +include_directories(${EVENT_INCLUDE_DIR}) +file(GLOB_RECURSE USCXML_IO_PROCESSOR_LIBEVENT + src/uscxml/ioprocessor/basichttp/libevent/*.cpp + src/uscxml/ioprocessor/basichttp/libevent/*.h +) +list (APPEND USCXML_FILES ${USCXML_IO_PROCESSOR_LIBEVENT}) + +file(GLOB_RECURSE USCXML_EVENTQUEUE_LIBEVENT + src/uscxml/concurrency/eventqueue/libevent/*.cpp + src/uscxml/concurrency/eventqueue/libevent/*.h +) +list (APPEND USCXML_FILES ${USCXML_EVENTQUEUE_LIBEVENT}) + +list (APPEND USCXML_LIBS ${EVENT_LIBRARY}) + +# v8 +set(ENV{V8_SRC} ${CMAKE_SOURCE_DIR}/../v8) +find_package(V8 REQUIRED) +include_directories(${V8_INCLUDE_DIR}) +file(GLOB_RECURSE USCXML_DATAMODEL_V8 + src/uscxml/datamodel/ecmascript/v8/*.cpp + src/uscxml/datamodel/ecmascript/v8/*.h +) +list (APPEND USCXML_FILES ${USCXML_DATAMODEL_V8}) +list (APPEND USCXML_LIBS ${V8_LIBRARY}) + + +file(GLOB USCXML_CONCURRENCY src/uscxml/concurrency/*.cpp src/uscxml/concurrency/*.h) +list (APPEND USCXML_FILES ${USCXML_CONCURRENCY}) + +file(GLOB USCXML_INTERPRETER src/uscxml/*.cpp src/uscxml/*.h) +list (APPEND USCXML_FILES ${USCXML_INTERPRETER}) + +include_directories(src) + +# build library +add_library(uscxml ${USCXML_FILES}) +target_link_libraries(uscxml ${USCXML_LIBS}) + +add_subdirectory(test) +add_subdirectory(src/bindings) + +# ---- OLD DEPENDENCIES ------------- + +#set(Boost_DEBUG 1) +# find_package(Boost COMPONENTS thread regex system date_time REQUIRED) +# INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR}) + +#openssl - needed for pion +# find_package(OPENSSL REQUIRED) +# INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) + +#zlib - needed for pion +# find_package(ZLIB REQUIRED) +# INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS}) + +#iconv - needed for pion +# find_package(ICONV REQUIRED) +# INCLUDE_DIRECTORIES(${ICONV_INCLUDE_DIR}) + +#ev - no longer supported +# set(ENV{EV_SRC} ${CMAKE_SOURCE_DIR}/../libev) +# find_package(EV REQUIRED) +# include_directories(${EV_INCLUDE_DIR}) + +#pion +# set(ENV{PION_SRC} ${CMAKE_SOURCE_DIR}/../pion) +# find_package(PION REQUIRED) +# include_directories(${PION_INCLUDE_DIR}) +# file(GLOB_RECURSE USCXML_IO_PROCESSOR_PION +# src/uscxml/ioprocessor/basichttp/pion/*.cpp +# src/uscxml/ioprocessor/basichttp/pion/*.h +# ) + diff --git a/contrib/cmake/FindArabica.cmake b/contrib/cmake/FindArabica.cmake new file mode 100644 index 0000000..bd4fe18 --- /dev/null +++ b/contrib/cmake/FindArabica.cmake @@ -0,0 +1,21 @@ +FIND_PATH(ARABICA_INCLUDE_DIR Arabica/getparam.hpp + PATH_SUFFIXES include/arabica include + PATHS + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt + HINTS $ENV{ARABICA_SRC} +) + +FIND_LIBRARY(ARABICA_LIBRARY + NAMES arabica + HINTS $ENV{ARABICA_SRC}/src/.libs/ + +) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Arabica DEFAULT_MSG ARABICA_LIBRARY ARABICA_INCLUDE_DIR) +MARK_AS_ADVANCED(ARABICA_LIBRARY ARABICA_INCLUDE_DIR) diff --git a/contrib/cmake/FindEV.cmake b/contrib/cmake/FindEV.cmake new file mode 100644 index 0000000..f994aa1 --- /dev/null +++ b/contrib/cmake/FindEV.cmake @@ -0,0 +1,20 @@ +FIND_PATH(EV_INCLUDE_DIR ev.h + PATH_SUFFIXES include + PATHS + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt + HINTS $ENV{EV_SRC} +) + +FIND_LIBRARY(EV_LIBRARY + NAMES ev + HINTS $ENV{EV_SRC}/.libs/ +) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(EV DEFAULT_MSG EV_LIBRARY EV_INCLUDE_DIR) +MARK_AS_ADVANCED(EV_LIBRARY EV_INCLUDE_DIR) diff --git a/contrib/cmake/FindEVENT.cmake b/contrib/cmake/FindEVENT.cmake new file mode 100644 index 0000000..31e074c --- /dev/null +++ b/contrib/cmake/FindEVENT.cmake @@ -0,0 +1,34 @@ +FIND_PATH(EVENT_INCLUDE_DIR event2/event.h + PATH_SUFFIXES include + PATHS + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt + HINTS $ENV{EVENT_SRC} +) + +FIND_LIBRARY(EVENT_LIBRARY + NAMES event + HINTS $ENV{EVENT_SRC}/.libs/ +) + +if (NOT WIN32) + FIND_LIBRARY(EVENT_LIBRARY_THREADS + NAMES event_pthreads + HINTS $ENV{EVENT_SRC}/.libs/ + ) +else() + FIND_LIBRARY(EVENT_LIBRARY_THREADS + NAMES event_pthreads + HINTS $ENV{EVENT_SRC}/.libs/ + ) +endif() + +list (APPEND EVENT_LIBRARY ${EVENT_LIBRARY_THREADS}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(EVENT DEFAULT_MSG EVENT_LIBRARY EVENT_INCLUDE_DIR) +MARK_AS_ADVANCED(EVENT_LIBRARY EVENT_INCLUDE_DIR) diff --git a/contrib/cmake/FindGLOG.cmake b/contrib/cmake/FindGLOG.cmake new file mode 100644 index 0000000..fee9d78 --- /dev/null +++ b/contrib/cmake/FindGLOG.cmake @@ -0,0 +1,20 @@ +FIND_PATH(GLOG_INCLUDE_DIR glog/logging.h + PATH_SUFFIXES include src + PATHS + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt + HINTS $ENV{GLOG_SRC} +) + +FIND_LIBRARY(GLOG_LIBRARY + NAMES glog + HINTS $ENV{GLOG_SRC}/.libs/ +) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLOG DEFAULT_MSG GLOG_LIBRARY GLOG_INCLUDE_DIR) +MARK_AS_ADVANCED(GLOG_LIBRARY GLOG_INCLUDE_DIR) diff --git a/contrib/cmake/FindICONV.cmake b/contrib/cmake/FindICONV.cmake new file mode 100644 index 0000000..a95bc65 --- /dev/null +++ b/contrib/cmake/FindICONV.cmake @@ -0,0 +1,57 @@ +# - Try to find Iconv +# Once done this will define +# +# ICONV_FOUND - system has Iconv +# ICONV_INCLUDE_DIR - the Iconv include directory +# ICONV_LIBRARIES - Link these to use Iconv +# ICONV_SECOND_ARGUMENT_IS_CONST - the second argument for iconv() is const +# +include(CheckCXXSourceCompiles) + +IF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + # Already in cache, be silent + SET(ICONV_FIND_QUIETLY TRUE) +ENDIF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + +FIND_PATH(ICONV_INCLUDE_DIR iconv.h) + +FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2 c) + +IF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + SET(ICONV_FOUND TRUE) +ENDIF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + +set(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR}) +set(CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARIES}) +IF(ICONV_FOUND) + check_cxx_source_compiles(" + #include <iconv.h> + int main(){ + iconv_t conv = 0; + const char* in = 0; + size_t ilen = 0; + char* out = 0; + size_t olen = 0; + iconv(conv, &in, &ilen, &out, &olen); + return 0; + } +" ICONV_SECOND_ARGUMENT_IS_CONST ) +ENDIF(ICONV_FOUND) +set(CMAKE_REQUIRED_INCLUDES) +set(CMAKE_REQUIRED_LIBRARIES) + +IF(ICONV_FOUND) + IF(NOT ICONV_FIND_QUIETLY) + MESSAGE(STATUS "Found Iconv: ${ICONV_LIBRARIES}") + ENDIF(NOT ICONV_FIND_QUIETLY) +ELSE(ICONV_FOUND) + IF(Iconv_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find Iconv") + ENDIF(Iconv_FIND_REQUIRED) +ENDIF(ICONV_FOUND) + +MARK_AS_ADVANCED( + ICONV_INCLUDE_DIR + ICONV_LIBRARIES + ICONV_SECOND_ARGUMENT_IS_CONST +)
\ No newline at end of file diff --git a/contrib/cmake/FindPION.cmake b/contrib/cmake/FindPION.cmake new file mode 100644 index 0000000..3a4157a --- /dev/null +++ b/contrib/cmake/FindPION.cmake @@ -0,0 +1,20 @@ +FIND_PATH(PION_INCLUDE_DIR pion/config.hpp + PATH_SUFFIXES include + PATHS + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt + HINTS $ENV{PION_SRC} +) + +FIND_LIBRARY(PION_LIBRARY + NAMES pion + HINTS $ENV{PION_SRC}/src/.libs/ +) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PION DEFAULT_MSG PION_LIBRARY PION_INCLUDE_DIR) +MARK_AS_ADVANCED(PION_LIBRARY PION_INCLUDE_DIR) diff --git a/contrib/cmake/FindV8.cmake b/contrib/cmake/FindV8.cmake new file mode 100644 index 0000000..6836fb3 --- /dev/null +++ b/contrib/cmake/FindV8.cmake @@ -0,0 +1,28 @@ +FIND_PATH(V8_INCLUDE_DIR v8.h + PATH_SUFFIXES include + PATHS + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt + HINTS $ENV{V8_SRC} +) + +FIND_LIBRARY(V8_LIBRARY_BASE + NAMES v8_base + HINTS $ENV{V8_SRC}/out/native/ +) +list(APPEND V8_LIBRARY ${V8_LIBRARY_BASE}) + +FIND_LIBRARY(V8_LIBRARY_SNAPSHOT + NAMES v8_snapshot + HINTS $ENV{V8_SRC}/out/native/ + +) +list(APPEND V8_LIBRARY ${V8_LIBRARY_SNAPSHOT}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(V8 DEFAULT_MSG V8_LIBRARY V8_INCLUDE_DIR) +MARK_AS_ADVANCED(V8_LIBRARY V8_INCLUDE_DIR) diff --git a/src/bindings/CMakeLists.txt b/src/bindings/CMakeLists.txt new file mode 100644 index 0000000..deac396 --- /dev/null +++ b/src/bindings/CMakeLists.txt @@ -0,0 +1,12 @@ +find_package(SWIG) +if (SWIG_FOUND) + if(SWIG_VERSION VERSION_GREATER 2.0.4) + MARK_AS_ADVANCED(SWIG_DIR SWIG_EXECUTABLE SWIG_VERSION) + INCLUDE(${SWIG_USE_FILE}) + add_subdirectory(swig/java) + else() + message("SWIG version 2.0.5 is required, found ${SWIG_VERSION} - skipping java wrapper generation") + endif() +else() + message("SWIG not found - skipping wrapper generation") +endif() diff --git a/src/bindings/swig/java/CMakeLists.txt b/src/bindings/swig/java/CMakeLists.txt new file mode 100644 index 0000000..a729b8e --- /dev/null +++ b/src/bindings/swig/java/CMakeLists.txt @@ -0,0 +1,31 @@ +# generate JNI library and create a jar +# Make from within Eclipse fails miserably with the whole thing + +find_package(JNI) +if(JNI_FOUND) + include_directories(${JNI_INCLUDE_DIRS}) +else() + message(STATUS "No JNI libraries found - not building Java wrappers") + return() +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +SET(CMAKE_SWIG_FLAGS "") +SET(SCXMLPL_JAVA_PACKAGE "org.uscxml") +SET(SCXMLPL_JAVA_DIR "org/uscxml") + +# we need ; to produce a space with the package .. weird +SET_SOURCE_FILES_PROPERTIES(uscxml.i PROPERTIES SWIG_FLAGS "-package;${SCXMLPL_JAVA_PACKAGE}") +SET_SOURCE_FILES_PROPERTIES(uscxml.i PROPERTIES CPLUSPLUS ON) +SET(CMAKE_SWIG_OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/${SCXMLPL_JAVA_DIR}") + +SWIG_ADD_MODULE(uscxmlNativeJava java uscxml.i) +foreach(JNI_LIBRARY ${JNI_LIBRARIES}) + if (NOT ${JNI_LIBRARY} MATCHES ".*jawt.*") + SWIG_LINK_LIBRARIES(uscxmlNativeJava ${JNI_LIBRARY}) + endif() +endforeach() +set_target_properties(uscxmlNativeJava PROPERTIES FOLDER "Bindings") + +swig_link_libraries(uscxmlNativeJava uscxml) diff --git a/src/bindings/swig/java/uscxml.i b/src/bindings/swig/java/uscxml.i new file mode 100644 index 0000000..ccd6fe0 --- /dev/null +++ b/src/bindings/swig/java/uscxml.i @@ -0,0 +1,40 @@ +%module(directors="1", allprotected="1") uscxmlNativeJava + +// import swig typemaps +//%include <arrays_java.i> +//%include <inttypes.i> +%include <boost_shared_ptr.i> + +// disable warning related to unknown base class +#pragma SWIG nowarn=401 +//%ignore boost::enable_shared_from_this; + +%javaconst(1); + +# %shared_ptr(uscxml::dom::Element); +# %shared_ptr(uscxml::dom::Executable); + + +//************************************************** +// This ends up in the generated wrapper code +//************************************************** + +%{ + +#include "../../../uscxml/Message.h" +#include "../../../uscxml/Interpreter.h" + +using namespace uscxml; + +%} + +%rename(toString) operator<<; + + +//*********************************************** +// Parse the header file to generate wrappers +//*********************************************** + +%include "../../../uscxml/Message.h" +%include "../../../uscxml/Interpreter.h" + diff --git a/src/uscxml.h b/src/uscxml.h new file mode 100644 index 0000000..792ba43 --- /dev/null +++ b/src/uscxml.h @@ -0,0 +1,6 @@ +#ifndef USCXML_H_2WZ0PBQH +#define USCXML_H_2WZ0PBQH + +#include "uscxml/Interpreter.h" + +#endif /* end of include guard: USCXML_H_2WZ0PBQH */ diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp new file mode 100644 index 0000000..91da562 --- /dev/null +++ b/src/uscxml/Factory.cpp @@ -0,0 +1,56 @@ +#include "uscxml/Factory.h" +#include "uscxml/datamodel/ecmascript/v8/V8DataModel.h" +//#include "uscxml/ioprocessor/basichttp/pion/PionIOProcessor.h" +#include "uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.h" + +namespace uscxml { + + Factory::Factory() { + _dataModels["ecmascript"] = new V8DataModel(); +// _ioProcessors["basichttp"] = new PionIOProcessor(); + _ioProcessors["basichttp"] = new io::libevent::EventIOProcessor(); + } + + void Factory::registerIOProcessor(const std::string type, IOProcessor* ioProcessor) { + getInstance()->_ioProcessors[type] = ioProcessor; + } + + void Factory::registerDataModel(const std::string type, DataModel* dataModel) { + getInstance()->_dataModels[type] = dataModel; + } + + void Factory::registerExecutableContent(const std::string tag, ExecutableContent* executableContent) { + getInstance()->_executableContent[tag] = executableContent; + } + + DataModel* Factory::getDataModel(const std::string type, Interpreter* interpreter) { + if (Factory::getInstance()->_dataModels.find(type) != getInstance()->_dataModels.end()) { + return getInstance()->_dataModels[type]->create(interpreter); + } + return NULL; + } + + IOProcessor* Factory::getIOProcessor(const std::string type, Interpreter* interpreter) { + if (getInstance()->_ioProcessors.find(type) != getInstance()->_ioProcessors.end()) { + return getInstance()->_ioProcessors[type]->create(interpreter); + } + return NULL; + } + + ExecutableContent* Factory::getExecutableContent(const std::string tag, Interpreter* interpreter) { + if (getInstance()->_executableContent.find(tag) != getInstance()->_executableContent.end()) { + return getInstance()->_executableContent[tag]->create(interpreter); + } + return NULL; + } + + Factory* Factory::getInstance() { + if (_instance == NULL) { + _instance = new Factory(); + } + return _instance; + } + + Factory* Factory::_instance = NULL; + +}
\ No newline at end of file diff --git a/src/uscxml/Factory.h b/src/uscxml/Factory.h new file mode 100644 index 0000000..f1840c6 --- /dev/null +++ b/src/uscxml/Factory.h @@ -0,0 +1,73 @@ +#ifndef FACTORY_H_5WKLGPRB +#define FACTORY_H_5WKLGPRB + +#include "uscxml/Message.h" + +#include <string> + +namespace uscxml { + + class Interpreter; + + class ExecutableContent { + public: + ExecutableContent() {}; + virtual ExecutableContent* create(Interpreter* interpreter) = 0; + }; + + class IOProcessor { + public: + IOProcessor() {}; + virtual ~IOProcessor() {}; + virtual IOProcessor* create(Interpreter* interpreter) = 0; + + virtual std::string getURL() = 0; + virtual void send(SendRequest& req) = 0; + virtual void invoke(InvokeRequest& req) = 0; + virtual void cancel(const std::string sendId) = 0; + }; + + class DataModel { + public: + virtual DataModel* create(Interpreter* interpreter) = 0; + + virtual bool validate(const std::string& location, const std::string& schema) = 0; + virtual void setEvent(Event& event) = 0; + virtual void setData(const std::string& key, Data& event) = 0; + + // foreach + virtual uint32_t getLength(const std::string& expr) = 0; + virtual void pushContext() = 0; + virtual void popContext() = 0; + + 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 std::string& expr) = 0; + }; + + class Factory { + public: + static void registerIOProcessor(const std::string type, IOProcessor* ioProcessor); + static void registerDataModel(const std::string type, DataModel* dataModel); + static void registerExecutableContent(const std::string tag, ExecutableContent* executableContent); + + static DataModel* getDataModel(const std::string type, Interpreter* interpreter); + static IOProcessor* getIOProcessor(const std::string type, Interpreter* interpreter); + static ExecutableContent* getExecutableContent(const std::string tag, Interpreter* interpreter); + static Factory* getInstance(); + + std::map<std::string, DataModel*> _dataModels; + std::map<std::string, IOProcessor*> _ioProcessors; + std::map<std::string, ExecutableContent*> _executableContent; + + protected: + Factory(); + static Factory* _instance; + + }; + + +} + +#endif /* end of include guard: FACTORY_H_5WKLGPRB */ diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp new file mode 100644 index 0000000..7189f12 --- /dev/null +++ b/src/uscxml/Interpreter.cpp @@ -0,0 +1,1712 @@ +#include "uscxml/Interpreter.h" + +#include <DOM/Simple/DOMImplementation.hpp> + +#include <boost/uuid/uuid.hpp> +#include <boost/uuid/uuid_generators.hpp> +#include <boost/uuid/uuid_io.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/tokenizer.hpp> + +#include <glog/logging.h> + +#include <assert.h> +#include <algorithm> + +namespace uscxml { + +using namespace Arabica::XPath; +using namespace Arabica::DOM; + +boost::uuids::random_generator Interpreter::uuidGen; +const std::string Interpreter::getUUID() { + return boost::lexical_cast<std::string>(uuidGen()); +} + +Interpreter::Interpreter(const std::string& url) { + _thread = NULL; + _url = url; + + LOG(INFO) << "runtime started with " << _url; + Arabica::SAX::InputSource<std::string> inputSource(_url); + + Arabica::SAX2DOM::Parser<std::string> domParser; + Arabica::SAX::CatchErrorHandler<std::string> errorHandler; + domParser.setErrorHandler(errorHandler); + if(!domParser.parse(inputSource) || !domParser.getDocument().hasChildNodes()) { + LOG(INFO) << "could not parse " << _url << ":"; + if(errorHandler.errorsReported()) { + LOG(ERROR) << errorHandler.errors() << std::endl; + } else { + Arabica::SAX::InputSourceResolver resolver(inputSource, Arabica::default_string_adaptor<std::string>()); + if (!resolver.resolve()) { + LOG(ERROR) << "no such file"; + } + } + } else { + _doc = domParser.getDocument(); + } + + if (_doc) { + // do we have a xmlns attribute? + std::string ns = _doc.getDocumentElement().getNamespaceURI(); + if(ns.size() > 0) { + _nsContext.addNamespaceDeclaration(ns, "sc"); + _xpath.setNamespaceContext(_nsContext); + _nsPrefix = "sc:"; + } + NodeList<std::string> scxmls = _doc.getElementsByTagName("scxml"); + if (scxmls.getLength() > 0) { + _scxml = (Arabica::DOM::Element<std::string>)scxmls.item(0); + } else { + LOG(ERROR) << "Cannot find SCXML element" << std::endl; + } + _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : getUUID()); + } +} + +Interpreter::~Interpreter() { + std::map<std::string, IOProcessor*>::iterator ioProcessorIter = _ioProcessors.begin(); + while(ioProcessorIter != _ioProcessors.end()) { + delete ioProcessorIter->second; + ioProcessorIter++; + } + if (_thread) { + _thread->join(); + delete(_thread); + } +} + +void Interpreter::start() { + _thread = new tthread::thread(Interpreter::run, this); +} + +void Interpreter::stop() { +} + +void Interpreter::run(void* instance) { + ((Interpreter*)instance)->interpret(); +} + +void Interpreter::waitForStabilization() { + tthread::lock_guard<tthread::mutex> lock(_mutex); + _stabilized.wait(_mutex); +} + +// see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation +void Interpreter::interpret() { + if (!_scxml) + return; +// dump(); + + _sessionId = getUUID(); + normalize(_doc); + + if(HAS_ATTR(_scxml, "datamodel")) { + _dataModel = Factory::getDataModel(ATTR(_scxml, "datamodel"), this); + if(_dataModel == NULL) { + LOG(ERROR) << "No datamodel for " << ATTR(_scxml, "datamodel") << " registered"; + return; + } + } + + setupIOProcessors(); + + // executeGlobalScriptElements + NodeSet<std::string> globalScriptElems = _xpath.evaluate("/" + _nsPrefix + "scxml/" + _nsPrefix + "script", _doc).asNodeSet(); + for (unsigned int i = 0; i < globalScriptElems.size(); i++) { +// std::cout << globalScriptElems[i].getFirstChild().getNodeValue() << std::endl; + if (_dataModel) + executeContent(globalScriptElems[i]); + } + + _running = true; + + std::string binding = _xpath.evaluate("/" + _nsPrefix + "scxml/@binding", _doc).asString(); + _binding = (boost::iequals(binding, "late") ? LATE : EARLY); + + // initialize all data elements + if (_dataModel && _binding == EARLY) { + NodeSet<std::string> dataElems = _xpath.evaluate("//" + _nsPrefix + "data", _doc).asNodeSet(); + for (unsigned int i = 0; i < dataElems.size(); i++) { + initializeData(dataElems[i]); + } + } else if(_dataModel) { + NodeSet<std::string> topDataElems = _xpath.evaluate("/" + _nsPrefix + "scxml/" + _nsPrefix + "datamodel/" + _nsPrefix + "data", _doc).asNodeSet(); + for (unsigned int i = 0; i < topDataElems.size(); i++) { + initializeData(topDataElems[i]); + } + } + + // 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); + enterStates(initialTransitions); + + mainEventLoop(); +} + +void Interpreter::initializeData(const Arabica::DOM::Node<std::string>& data) { + if (!_dataModel) { + LOG(ERROR) << "Cannot initialize data when no datamodel is given!"; + return; + } + try { + if (!HAS_ATTR(data, "id")) + return; + std::string value = "undefined"; + + if (HAS_ATTR(data, "expr")) { + value = ATTR(data, "expr"); + goto VALUE_INITIALIZED; + } + if (HAS_ATTR(data, "src")) { + Arabica::SAX::InputSourceResolver resolver(Arabica::SAX::InputSource<std::string>(ATTR(data, "src")), + Arabica::default_string_adaptor<std::string>()); + value = std::string(std::istreambuf_iterator<char>(*resolver.resolve()), std::istreambuf_iterator<char>()); + goto VALUE_INITIALIZED; + } + if (data.hasChildNodes()) { + // search for the text node with the actual script + if (data.getFirstChild().getNodeType() == Node_base::TEXT_NODE) { + value = data.getFirstChild().getNodeValue(); + } + } + +VALUE_INITIALIZED: + _dataModel->assign(ATTR(data, "id"), value); + + } catch (Event e) { + LOG(ERROR) << "Syntax error in send element:" << std::endl << e << std::endl; + } +} + +void Interpreter::normalize(const Arabica::DOM::Node<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++) { + Arabica::DOM::Element<std::string> stateElem = Arabica::DOM::Element<std::string>(states[i]); + stateElem.setAttribute("isFirstEntry", "true"); + if (!stateElem.hasAttribute("id")) { + stateElem.setAttribute("id", getUUID()); + } + } + + Arabica::XPath::NodeSet<std::string> finals = _xpath.evaluate("//" + _nsPrefix + "final", _doc).asNodeSet(); + for (int i = 0; i < finals.size(); i++) { + Arabica::DOM::Element<std::string> finalElem = Arabica::DOM::Element<std::string>(finals[i]); + finalElem.setAttribute("isFirstEntry", "true"); + if (!finalElem.hasAttribute("id")) { + finalElem.setAttribute("id", getUUID()); + } + } + + Arabica::XPath::NodeSet<std::string> histories = _xpath.evaluate("//" + _nsPrefix + "history", _doc).asNodeSet(); + for (int i = 0; i < histories.size(); i++) { + Arabica::DOM::Element<std::string> historyElem = Arabica::DOM::Element<std::string>(histories[i]); + if (!historyElem.hasAttribute("id")) { + historyElem.setAttribute("id", getUUID()); + } + } + + Arabica::XPath::NodeSet<std::string> scxml = _xpath.evaluate("/" + _nsPrefix + "scxml", _doc).asNodeSet(); + if (!((Arabica::DOM::Element<std::string>)scxml[0]).hasAttribute("id")) { + ((Arabica::DOM::Element<std::string>)scxml[0]).setAttribute("id", getUUID()); + } +} + +void Interpreter::mainEventLoop() { + while(_running) { + NodeSet<std::string> enabledTransitions; + _stable = false; + + // Here we handle eventless transitions and transitions + // triggered by internal events until machine is stable + while(_running && !_stable) { +#if 0 + std::cout << "Configuration: "; + for (int i = 0; i < _configuration.size(); i++) { + std::cout << ((Arabica::DOM::Element<std::string>)_configuration[i]).getAttribute("id") << ", "; + } + std::cout << std::endl; +#endif + enabledTransitions = selectEventlessTransitions(); + if (enabledTransitions.size() == 0) { + if (_internalQueue.size() == 0) { + _stable = true; + } else { + Event internalEvent = _internalQueue.front(); + _internalQueue.pop_front(); + _dataModel->setEvent(internalEvent); + enabledTransitions = selectTransitions(internalEvent.name); + } + } + if (!enabledTransitions.empty()) + microstep(enabledTransitions); + } + + for (unsigned int i = 0; i < _statesToInvoke.size(); i++) { + NodeSet<std::string> invokes = _xpath.evaluate("" + _nsPrefix + "invoke", _statesToInvoke[i]).asNodeSet(); + for (unsigned int j = 0; j < invokes.size(); j++) { + // @TODO: + // boost::shared_ptr<Invoke> invoke = boost::static_pointer_cast<Invoke>(interpreter::Factory::create("invoke", invokes[j])); + // invoke->invoke(this); + } + } + + _statesToInvoke = NodeSet<std::string>(); + if (!_internalQueue.empty()) + continue; + + { + tthread::lock_guard<tthread::mutex> lock(_mutex); + _stabilized.notify_all(); + } + + Event externalEvent = _externalQueue.pop(); + if (!_running) + exitInterpreter(); + + if (_dataModel && boost::iequals(externalEvent.name, "cancel.invoke." + _sessionId)) + break; + + if (_dataModel) + _dataModel->setEvent(externalEvent); + for (unsigned int i = 0; i < _configuration.size(); i++) { + NodeSet<std::string> invokes = _xpath.evaluate("" + _nsPrefix + "invoke", _configuration[i]).asNodeSet(); + for (unsigned int j = 0; j < invokes.size(); j++) { + std::string invokeId = ((Arabica::DOM::Element<std::string>)invokes[i]).getAttribute("id"); + std::string autoForward = ((Arabica::DOM::Element<std::string>)invokes[i]).getAttribute("autoforward"); + if (boost::iequals(invokeId, externalEvent.invokeid)) { + // @TODO + // boost::shared_ptr<Invoke> invoke = boost::static_pointer_cast<Invoke>(interpreter::Factory::create("invoke", invokes[j])); + // invoke->applyFinalize(this); + } + if (autoForward.length() > 0) { + send(invokeId, externalEvent); + } + } + } + enabledTransitions = selectTransitions(externalEvent.name); + if (!enabledTransitions.empty()) + microstep(enabledTransitions); + } + exitInterpreter(); +} + +void Interpreter::send(const std::string invokeId, Event& event) { +} + +void Interpreter::send(const Arabica::DOM::Node<std::string>& element) { + SendRequest sendReq; + try { + // event + if (HAS_ATTR(element, "eventexpr") && _dataModel) { + sendReq.name = _dataModel->evalAsString(ATTR(element, "eventexpr")); + } else if (HAS_ATTR(element, "event")) { + sendReq.name = ATTR(element, "event"); + } else { + LOG(ERROR) << "send element does not feature event, or eventexpr without datamodel given"; + return; + } + // target + if (HAS_ATTR(element, "targetexpr") && _dataModel) { + sendReq.target = _dataModel->evalAsString(ATTR(element, "targetexpr")); + } else if (HAS_ATTR(element, "target")) { + sendReq.target = ATTR(element, "target"); + } + // type + if (HAS_ATTR(element, "typeexpr") && _dataModel) { + sendReq.type = _dataModel->evalAsString(ATTR(element, "typeexpr")); + } else if (HAS_ATTR(element, "type")) { + sendReq.type = ATTR(element, "type"); + } else { + sendReq.type = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor"; + } + // id + if (HAS_ATTR(element, "idlocation") && _dataModel) { + sendReq.sendid = _dataModel->evalAsString(ATTR(element, "idlocation")); + } else if (HAS_ATTR(element, "id")) { + sendReq.sendid = ATTR(element, "id"); + } else { + /* + * The ids for <send> and <invoke> are subtly different. In a conformant + * SCXML document, they must be unique within the session, but in the case + * where the author does not provide them, the processor must generate a + * new unique ID not at load time but each time the element is executed. + * Furthermore the attribute 'idlocation' can be used to capture this + * automatically generated id. Finally note that the automatically generated + * id for <invoke> has a special format. See 6.4.1 Attribute Details for + * details. The SCXML processor may generate all other ids in any format, + * as long as they are unique. + */ + sendReq.sendid = getUUID(); + } + /** @TODO: + * + * If 'idlocation' is present, the SCXML Processor must generate an id when + * the parent <send> element is evaluated and store it in this location. + * See 3.14 IDs for details. + * + */ + + // delay + std::string delay; + sendReq.delayMs = 0; + if (HAS_ATTR(element, "delayexpr") && _dataModel) { + delay = _dataModel->evalAsString(ATTR(element, "delayexpr")); + } else if (HAS_ATTR(element, "delay")) { + delay = ATTR(element, "delay"); + } + if (delay.size() > 0) { + boost::trim(delay); + std::stringstream delayTime; + if (delay.size() > 2 && boost::iequals("ms", delay.substr(delay.length() - 2, 2))) { + delayTime << delay.substr(0, delay.size() - 2); + delayTime >> sendReq.delayMs; + } else if (delay.size() > 1 && boost::iequals("s", delay.substr(delay.length() - 1, 1))) { + delayTime << delay.substr(0, delay.size() - 1); + delayTime >> sendReq.delayMs; + sendReq.delayMs *= 1000; + } else { + LOG(ERROR) << "Cannot make sense of delay value " << delay << ": does not end in 's' or 'ms'"; + } + } + // namelist + if (HAS_ATTR(element, "namelist")) { + std::vector<std::string> names = tokenizeIdRefs(ATTR(element, "namelist")); + for (int i = 0; i < names.size(); i++) { + sendReq.namelist[names[i]] = _dataModel->evalAsString(names[i]); + } + } + + // 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")) { + LOG(ERROR) << "param element is missing name attribut"; + continue; + } + std::string paramValue; + if (HAS_ATTR(params[i], "expr") && _dataModel) { + std::string location = _dataModel->evalAsString(ATTR(params[i], "expr")); + paramValue = _dataModel->evalAsString(location); + } else if(HAS_ATTR(params[i], "location") && _dataModel) { + paramValue = _dataModel->evalAsString(ATTR(params[i], "location")); + } else { + LOG(ERROR) << "param element is missing expr or location or no datamodel is specified"; + continue; + } + sendReq.params[ATTR(params[i], "name")] = paramValue; + } + + // content + NodeSet<std::string> contents = _xpath.evaluate("" + _nsPrefix + "content", element).asNodeSet(); + if (contents.size() > 1) + LOG(ERROR) << "Only a single content element is allowed for send elements - using first one"; + if (contents.size() > 0) { + if (HAS_ATTR(contents[0], "expr")) { + if (_dataModel) { + sendReq.content = _dataModel->evalAsString(ATTR(contents[0], "expr")); + } else { + LOG(ERROR) << "content element has expr attribute but no datamodel is specified."; + } + } else if (contents[0].hasChildNodes()) { +// dump(contents[0].getFirstChild()); + sendReq.content = contents[0].getFirstChild().getNodeValue(); + } else { + LOG(ERROR) << "content element does not specify any content."; + } + } + + IOProcessor* ioProc = getIOProcessor(sendReq.type); + if (ioProc != NULL) { + _ioProcessorsIds[sendReq.sendid] = ioProc; + ioProc->send(sendReq); + } + + } catch (Event e) { + LOG(ERROR) << "Syntax error in send element:" << std::endl << e << std::endl; + } +} + +void Interpreter::invoke(const Arabica::DOM::Node<std::string>& element) { + InvokeRequest invokeReq; + try { + // type + if (HAS_ATTR(element, "typeexpr") && _dataModel) { + invokeReq.type = _dataModel->evalAsString(ATTR(element, "typeexpr")); + } else if (HAS_ATTR(element, "type")) { + invokeReq.type = ATTR(element, "type"); + } else { + LOG(ERROR) << "invoke element is missing expr or typeexpr or no datamodel is specified"; + } + + // src + if (HAS_ATTR(element, "srcexpr") && _dataModel) { + invokeReq.src = _dataModel->evalAsString(ATTR(element, "srcexpr")); + } else if (HAS_ATTR(element, "src")) { + invokeReq.src = ATTR(element, "src"); + } else { + LOG(ERROR) << "invoke element is missing src or srcexpr or no datamodel is specified"; + } + + // id + if (HAS_ATTR(element, "idlocation") && _dataModel) { + invokeReq.invokeid = _dataModel->evalAsString(ATTR(element, "idlocation")); + } else if (HAS_ATTR(element, "id")) { + invokeReq.invokeid = ATTR(element, "id"); + } else { + invokeReq.invokeid = getUUID(); + } + + // namelist + if (HAS_ATTR(element, "namelist")) { + invokeReq.namelist = ATTR(element, "namelist"); + } + + // autoforward + if (HAS_ATTR(element, "autoforward")) { + if (boost::iequals(ATTR(element, "autoforward"), "true")) { + invokeReq.autoForward = true; + } + } else { + invokeReq.autoForward = false; + } + + // 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")) { + 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")); + } else if(HAS_ATTR(params[i], "location") && _dataModel) { + paramValue = _dataModel->evalAsString(ATTR(params[i], "location")); + } else { + LOG(ERROR) << "param element is missing expr or location or no datamodel is specified"; + continue; + } + invokeReq.params[ATTR(params[i], "name")] = paramValue; + } + + // content + NodeSet<std::string> contents = _xpath.evaluate("" + _nsPrefix + "content", element).asNodeSet(); + if (contents.size() > 1) + LOG(ERROR) << "Only a single content element is allowed for send elements - using first one"; + if (contents.size() > 0) { + invokeReq.content = contents[0].getNodeValue(); + } + + IOProcessor* ioProc = getIOProcessor(invokeReq.type); + if (ioProc != NULL) { + _ioProcessorsIds[invokeReq.invokeid] = ioProc; + ioProc->invoke(invokeReq); + } + + } catch (Event e) { + LOG(ERROR) << "Syntax error in invoke element:" << std::endl << e << std::endl; + } + +} + +Arabica::XPath::NodeSet<std::string> Interpreter::selectTransitions(const std::string& event) { + Arabica::XPath::NodeSet<std::string> enabledTransitions; + + NodeSet<std::string> atomicStates; + for (unsigned int i = 0; i < _configuration.size(); i++) { + if (isAtomic(_configuration[i])) + atomicStates.push_back(_configuration[i]); + } + atomicStates.to_document_order(); + + for (unsigned int i = 0; i < atomicStates.size(); i++) { + NodeSet<std::string> ancestors = getProperAncestors(atomicStates[i], Arabica::DOM::Node<std::string>()); + ancestors.push_back(atomicStates[i]); + for (unsigned int j = 0; j < ancestors.size(); j++) { + NodeSet<std::string> transitions = _xpath.evaluate("" + _nsPrefix + "transition", ancestors[j]).asNodeSet(); + for (unsigned int k = 0; k < transitions.size(); k++) { + if (((Arabica::DOM::Element<std::string>)transitions[k]).hasAttribute("event") && + nameMatch(((Arabica::DOM::Element<std::string>)transitions[k]).getAttribute("event"), event) && + hasConditionMatch(transitions[k])) { + enabledTransitions.push_back(transitions[k]); + goto LOOP; + } + } + } + LOOP:; + } + return enabledTransitions; +} + +// see: http://www.w3.org/TR/scxml/#EventDescriptors +bool Interpreter::nameMatch(const std::string& transitionEvent, const std::string& event) { + assert(transitionEvent.size() > 0); + assert(event.size() > 0); + + // naive case of single descriptor and exact match + if (boost::equals(transitionEvent, event)) + return true; + + boost::char_separator<char> sep(" "); + boost::tokenizer<boost::char_separator<char> > tokens(transitionEvent, sep); + boost::tokenizer<boost::char_separator<char> >::iterator tokenIter = tokens.begin(); + + while(tokenIter != tokens.end()) { + std::string eventDesc(*tokenIter++); + + // remove optional trailing .* for CCXML compatibility + if (eventDesc.find("*", eventDesc.size() - 1) != std::string::npos) + eventDesc = eventDesc.substr(0, eventDesc.size() - 1); + if (eventDesc.find(".", eventDesc.size() - 1) != std::string::npos) + eventDesc = eventDesc.substr(0, eventDesc.size() - 1); + + // are they already equal? + if (boost::equals(eventDesc, event)) + return true; + + // eventDesc has to be a real prefix of event now and therefore shorter + if (eventDesc.size() >= event.size()) + continue; + + // it is a prefix of the event name and event continues with .something + if (eventDesc.compare(event.substr(0, eventDesc.size())) == 0) + if (event.find(".", eventDesc.size()) == eventDesc.size()) + return true; + } + return false; +} + +Arabica::XPath::NodeSet<std::string> Interpreter::selectEventlessTransitions() { + Arabica::XPath::NodeSet<std::string> enabledTransitions; + + NodeSet<std::string> atomicStates; + for (unsigned int i = 0; i < _configuration.size(); i++) { + if (isAtomic(_configuration[i])) + atomicStates.push_back(_configuration[i]); + } + atomicStates.to_document_order(); + + for (unsigned int i = 0; i < atomicStates.size(); i++) { + NodeSet<std::string> ancestors = getProperAncestors(atomicStates[i], Arabica::DOM::Node<std::string>()); + ancestors.push_back(atomicStates[i]); + for (unsigned int j = 0; j < ancestors.size(); j++) { + NodeSet<std::string> transitions = _xpath.evaluate("" + _nsPrefix + "transition", ancestors[j]).asNodeSet(); + for (unsigned int k = 0; k < transitions.size(); k++) { + if (!((Arabica::DOM::Element<std::string>)transitions[k]).hasAttribute("event") && hasConditionMatch(transitions[k])) { + enabledTransitions.push_back(transitions[k]); + goto LOOP; + } + } + } + LOOP:; + } + + enabledTransitions = filterPreempted(enabledTransitions); + return enabledTransitions; +} + +bool Interpreter::hasConditionMatch(const Arabica::DOM::Node<std::string>& conditional) { + try { + if (_dataModel && HAS_ATTR(conditional, "cond")) + return _dataModel->evalAsBool(ATTR(conditional, "cond")); + } catch (Event e) { + LOG(ERROR) << "Syntax error in cond attribute of " << TAGNAME(conditional) << " element:" << std::endl << e << std::endl; + return false; + } + return true; // no condition is always true +} + +Arabica::XPath::NodeSet<std::string> Interpreter::filterPreempted(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { + Arabica::XPath::NodeSet<std::string> filteredTransitions; + for (unsigned int i = 0; i < enabledTransitions.size(); i++) { + Arabica::DOM::Node<std::string> t = enabledTransitions[i]; + for (unsigned int j = i+1; j < enabledTransitions.size(); j++) { + Arabica::DOM::Node<std::string> t2 = enabledTransitions[j]; + if (isPreemptingTransition(t2, t)) + goto LOOP; + } + filteredTransitions.push_back(t); + LOOP:; + } + return filteredTransitions; +} + +bool Interpreter::isPreemptingTransition(const Arabica::DOM::Node<std::string>& t1, const Arabica::DOM::Node<std::string>& t2) { + if (t1 == t2) + return false; + if (isWithinSameChild(t1) && (!isTargetless(t2) && !isWithinSameChild(t2))) + return true; + if (!isTargetless(t1) && !isWithinSameChild(t1)) + return true; + return false; +} + +void Interpreter::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { +#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; + NodeSet<std::string> targetSet = getTargetStates(enabledTransitions[i]); + for (int j = 0; j < targetSet.size(); j++) { + std::cout << " " << ((Arabica::DOM::Element<std::string>)targetSet[i]).getAttribute("id") << std::endl; + } + } + std::cout << std::endl; +#endif + + exitStates(enabledTransitions); + executeTransitionContent(enabledTransitions); + enterStates(enabledTransitions); +} + +void Interpreter::exitInterpreter() { + NodeSet<std::string> statesToExit = _configuration; + statesToExit.to_document_order(); + statesToExit.reverse(); + + for (int i = 0; i < statesToExit.size(); i++) { + Arabica::XPath::NodeSet<std::string> onExitElems = _xpath.evaluate("" + _nsPrefix + "onexit", statesToExit[i]).asNodeSet(); + for (int j = 0; j < onExitElems.size(); j++) { + executeContent(onExitElems[j]); + } + Arabica::XPath::NodeSet<std::string> invokeElems = _xpath.evaluate("" + _nsPrefix + "invoke", statesToExit[i]).asNodeSet(); + for (int j = 0; j < invokeElems.size(); j++) { + cancelInvoke(invokeElems[j]); + } + if (isFinal(statesToExit[i]) && parentIsScxmlState(statesToExit[i])) { + returnDoneEvent(statesToExit[i]); + } + } + _configuration = NodeSet<std::string>(); +} + +void Interpreter::executeTransitionContent(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { + for (int i = 0; i < enabledTransitions.size(); i++) { + executeContent(enabledTransitions[i]); + } +} + +void Interpreter::executeContent(const NodeList<std::string>& content) { + for (unsigned int i = 0; i < content.getLength(); i++) { + if (content.item(i).getNodeType() != Node_base::ELEMENT_NODE) + continue; + executeContent(content.item(i)); + } +} + +void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content) { + if (content.getNodeType() != Node_base::ELEMENT_NODE) + return; + + if (false) { + } else if (boost::iequals(TAGNAME(content), "raise")) { + // --- RAISE -------------------------- + if (HAS_ATTR(content, "event")) { + Event event; + event.name = ATTR(content, "event"); + _internalQueue.push_back(event); + } + } else if (boost::iequals(TAGNAME(content), "if")) { + // --- IF / ELSEIF / ELSE -------------- + Arabica::DOM::Element<std::string> ifElem = (Arabica::DOM::Element<std::string>)content; + if(hasConditionMatch(ifElem)) { + // condition is true, execute all content up to an elseif, else or end + if (ifElem.hasChildNodes()) { + NodeList<std::string> childs = ifElem.getChildNodes(); + for (unsigned int i = 0; i < childs.getLength(); i++) { + if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE) + continue; + if (boost::iequals(TAGNAME(childs.item(i)), "elsif") || + boost::iequals(TAGNAME(childs.item(i)), "else")) + break; + executeContent(childs.item(i)); + } + } + } else { + // condition does not match - do we have an elsif? + if (ifElem.hasChildNodes()) { + NodeList<std::string> elseifElem = ifElem.getElementsByTagName("elseif"); + for (unsigned int i = 0; i < elseifElem.getLength(); i++) { + if (hasConditionMatch(elseifElem.item(i))) { + executeContent(elseifElem.item(i).getChildNodes()); + goto ELSIF_ELEM_MATCH; + } + } + NodeList<std::string> elseElem = ifElem.getElementsByTagName("else"); + if (elseElem.getLength() > 0) + executeContent(elseElem.item(0).getChildNodes()); + } + } + ELSIF_ELEM_MATCH:; + } else if (boost::iequals(TAGNAME(content), "elseif")) { + std::cerr << "Found single elsif to evaluate!" << std::endl; + } else if (boost::iequals(TAGNAME(content), "else")) { + std::cerr << "Found single else to evaluate!" << std::endl; + } else if (boost::iequals(TAGNAME(content), "foreach")) { + // --- FOREACH -------------------------- + if (_dataModel) { + if (HAS_ATTR(content, "array") && HAS_ATTR(content, "item")) { + std::string array = ATTR(content, "array"); + std::string item = ATTR(content, "item"); + std::string index = (HAS_ATTR(content, "index") ? ATTR(content, "index") : ""); + uint32_t iterations = _dataModel->getLength(array); + _dataModel->pushContext(); // copy old and enter new context + for (uint32_t iteration = 0; iteration < iterations; iteration++) { + { + // assign array element to item + std::stringstream ss; + ss << array << "[" << iteration << "]"; + _dataModel->assign(item, ss.str()); + } + if (index.length() > 0) { + // assign iteration element to index + std::stringstream ss; + ss << iteration; + _dataModel->assign(index,ss.str()); + } + if (content.hasChildNodes()) + executeContent(content.getChildNodes()); + } + _dataModel->popContext(); // leave stacked context + } else { + LOG(ERROR) << "Expected array and item attributes with foreach element!" << std::endl; + } + } + } else if (boost::iequals(TAGNAME(content), "log")) { + // --- LOG -------------------------- + Arabica::DOM::Element<std::string> logElem = (Arabica::DOM::Element<std::string>)content; + if (logElem.hasAttribute("expr")) { + if (_dataModel) { + try { + std::cout << _dataModel->evalAsString(logElem.getAttribute("expr")) << std::endl; + } catch (Event e) { + LOG(ERROR) << "Syntax error in expr attribute of log element:" << std::endl << e << std::endl; + } + } else { + std::cout << logElem.getAttribute("expr") << std::endl; + } + } + } else if (boost::iequals(TAGNAME(content), "assign")) { + // --- ASSIGN -------------------------- + if (_dataModel && HAS_ATTR(content, "location") && HAS_ATTR(content, "expr")) { + try { + _dataModel->assign(ATTR(content, "location"), ATTR(content, "expr")); + } catch (Event e) { + LOG(ERROR) << "Syntax error in attributes of assign element:" << std::endl << e << std::endl; + } + } + } else if (boost::iequals(TAGNAME(content), "validate")) { + // --- VALIDATE -------------------------- + if (_dataModel) { + std::string location = (HAS_ATTR(content, "location") ? ATTR(content, "location") : ""); + std::string schema = (HAS_ATTR(content, "schema") ? ATTR(content, "schema") : ""); + _dataModel->validate(location, schema); + } + } else if (boost::iequals(TAGNAME(content), "script")) { + // --- SCRIPT -------------------------- + if (_dataModel) { + if (HAS_ATTR(content, "src")) { + Arabica::SAX::InputSourceResolver resolver(Arabica::SAX::InputSource<std::string>(ATTR(content, "src")), + Arabica::default_string_adaptor<std::string>()); + std::string srcContent(std::istreambuf_iterator<char>(*resolver.resolve()), std::istreambuf_iterator<char>()); + try { + _dataModel->eval(srcContent); + } catch (Event e) { + LOG(ERROR) << "Syntax error while executing script element from '" << ATTR(content, "src") << "':" << std::endl << e << std::endl; + } + } else { + if (content.hasChildNodes()) { + // search for the text node with the actual script + if (content.getFirstChild().getNodeType() == Node_base::TEXT_NODE) { + try { + _dataModel->eval(content.getFirstChild().getNodeValue()); + } catch (Event e) { + LOG(ERROR) << "Syntax error while executing script element" << std::endl << e << std::endl; + } + } + } + } + } + } else if (boost::iequals(TAGNAME(content), "send")) { + // --- SEND -------------------------- + send(content); + } else if (boost::iequals(TAGNAME(content), "cancel")) { + // --- CANCEL -------------------------- + std::string sendId; + try { + if (HAS_ATTR(content, "sendidexpr")) { + sendId = _dataModel->evalAsString(ATTR(content, "sendidexpr")); + } else if(HAS_ATTR(content, "sendid")) { + sendId = ATTR(content, "sendid"); + } else { + LOG(ERROR) << "Expected sendidexpr or sendid attribute in cancel element"; + return; + } + + IOProcessor* ioProc = getIOProcessorForId(sendId); + if (ioProc != NULL) { + ioProc->cancel(sendId); + } + + } catch (Event e) { + LOG(ERROR) << "Syntax error while executing cancel element" << std::endl << e << std::endl; + } + + } else if (boost::iequals(TAGNAME(content), "invoke")) { + // --- INVOKE -------------------------- + } else { + NodeList<std::string> executable = content.getChildNodes(); + for (int i = 0; i < executable.getLength(); i++) { + executeContent(executable.item(i)); + } + } +} + +void Interpreter::cancelInvoke(const Arabica::DOM::Node<std::string>& content) { +} + +void Interpreter::returnDoneEvent(const Arabica::DOM::Node<std::string>& state) { +} + +void Interpreter::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { + NodeSet<std::string> statesToExit; + for (int i = 0; i < enabledTransitions.size(); i++) { + Arabica::DOM::Element<std::string> transition = ((Arabica::DOM::Element<std::string>)enabledTransitions[i]); + if (!isTargetless(transition)) { + std::string transitionType = (boost::iequals(transition.getAttribute("type"), "internal") ? "internal" : "external"); + NodeSet<std::string> tStates = getTargetStates(transition); + Arabica::DOM::Node<std::string> ancestor; + Arabica::DOM::Node<std::string> source = getSourceState(transition); + + bool allDescendants = true; + for (int j = 0; j < tStates.size(); j++) { + if (!isDescendant(tStates[j], source)) { + allDescendants = false; + break; + } + } + if (boost::iequals(transitionType, "internal") && + isCompound(source) && + allDescendants) + { + ancestor = source; + } else { + NodeSet<std::string> tmpStates; + tmpStates.push_back(source); + tmpStates.insert(tmpStates.end(), tStates.begin(), tStates.end()); + + ancestor = findLCCA(tmpStates); + } + + for (int j = 0; j < _configuration.size(); j++) { + if (isDescendant(_configuration[j], ancestor)) + statesToExit.push_back(_configuration[j]); + } + } + } + // remove statesToExit from _statesToInvoke + std::list<Arabica::DOM::Node<std::string> > tmp; + for (int i = 0; i < _statesToInvoke.size(); i++) { + if (!isMember(_statesToInvoke[i], statesToExit)) { + tmp.push_back(_statesToInvoke[i]); + } + } + _statesToInvoke = NodeSet<std::string>(); + _statesToInvoke.insert(_statesToInvoke.end(), tmp.begin(), tmp.end()); + + statesToExit.to_document_order(); + statesToExit.reverse(); + + for (int i = 0; i < statesToExit.size(); i++) { + NodeSet<std::string> historyElems = _xpath.evaluate("" + _nsPrefix + "history", statesToExit[i]).asNodeSet(); + for (int j = 0; j < historyElems.size(); j++) { + Arabica::DOM::Element<std::string> historyElem = (Arabica::DOM::Element<std::string>)historyElems[j]; + std::string historyType = (historyElem.hasAttribute("type") ? historyElem.getAttribute("type") : "shallow"); + NodeSet<std::string> historyNodes; + for (int k = 0; k < _configuration.size(); k++) { + if (boost::iequals(historyType, "deep")) { + if (isAtomic(_configuration[k]) && isDescendant(_configuration[k], statesToExit[i])) + historyNodes.push_back(_configuration[k]); + } else { + if (_configuration[k].getParentNode() == statesToExit[i]) + historyNodes.push_back(_configuration[k]); + } + } + _historyValue[historyElem.getAttribute("id")] = historyNodes; + } + } + + for (int i = 0; i < statesToExit.size(); i++) { + Arabica::XPath::NodeSet<std::string> onExitElems = _xpath.evaluate("" + _nsPrefix + "onexit", statesToExit[i]).asNodeSet(); + for (int j = 0; j < onExitElems.size(); j++) { + executeContent(onExitElems[j]); + } + Arabica::XPath::NodeSet<std::string> invokeElems = _xpath.evaluate("" + _nsPrefix + "invoke", statesToExit[i]).asNodeSet(); + for (int j = 0; j < invokeElems.size(); j++) { + cancelInvoke(invokeElems[j]); + } + } + +// std::cout << "States to Exit: "; +// for (int i = 0; i < statesToExit.size(); i++) { +// std::cout << ((Arabica::DOM::Element<std::string>)statesToExit[i]).getAttribute("id") << ", "; +// } +// std::cout << std::endl; + + // remove statesToExit from _configuration + tmp.clear(); + for (int i = 0; i < _configuration.size(); i++) { + if (!isMember(_configuration[i], statesToExit)) { + tmp.push_back(_configuration[i]); + } + } + _configuration = NodeSet<std::string>(); + _configuration.insert(_configuration.end(), tmp.begin(), tmp.end()); + + +} + +void Interpreter::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { + NodeSet<std::string> statesToEnter; + NodeSet<std::string> statesForDefaultEntry; + + for (int i = 0; i < enabledTransitions.size(); i++) { + Arabica::DOM::Element<std::string> transition = ((Arabica::DOM::Element<std::string>)enabledTransitions[i]); + if (!isTargetless(transition)) { + std::string transitionType = (boost::iequals(transition.getAttribute("type"), "internal") ? "internal" : "external"); + NodeSet<std::string> tStates = getTargetStates(transition); + Arabica::DOM::Node<std::string> ancestor; + Arabica::DOM::Node<std::string> source = getSourceState(transition); + assert(source); + + bool allDescendants = true; + for (int j = 0; j < tStates.size(); j++) { + if (!isDescendant(tStates[j], source)) { + allDescendants = false; + break; + } + } + if (boost::iequals(transitionType, "internal") && + isCompound(source) && + allDescendants) + { + ancestor = source; + } else { + NodeSet<std::string> tmpStates; + tmpStates.push_back(source); + tmpStates.insert(tmpStates.end(), tStates.begin(), tStates.end()); + + ancestor = findLCCA(tmpStates); + } + + for (int j = 0; j < tStates.size(); j++) { + addStatesToEnter(tStates[j], statesToEnter, statesForDefaultEntry); + } + + for (int j = 0; j < tStates.size(); j++) { + NodeSet<std::string> ancestors = getProperAncestors(tStates[j], ancestor); + for (int k = 0; k < ancestors.size(); k++) { + statesToEnter.push_back(ancestors[k]); + if(isParallel(ancestors[k])) { + NodeSet<std::string> childs = getChildStates(ancestors[k]); + for (int l = 0; l < childs.size(); l++) { + bool someIsDescendant = false; + for (int m = 0; m < statesToEnter.size(); m++) { + if (isDescendant(statesToEnter[m], childs[l])) { + someIsDescendant = true; + break; + } + } + if (!someIsDescendant) { + addStatesToEnter(childs[l], statesToEnter, statesForDefaultEntry); + } + } + } + } + } + } + } + statesToEnter.to_document_order(); + for (int i = 0; i < statesToEnter.size(); i++) { + Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)statesToEnter[i]; + _configuration.push_back(stateElem); + _statesToInvoke.push_back(stateElem); + if (_binding == LATE && stateElem.getAttribute("isFirstEntry").size() > 0) { + Arabica::XPath::NodeSet<std::string> dataModelElems = _xpath.evaluate("" + _nsPrefix + "datamodel", stateElem).asNodeSet(); + if(dataModelElems.size() > 0 && _dataModel) { + Arabica::XPath::NodeSet<std::string> dataElems = _xpath.evaluate("" + _nsPrefix + "data", dataModelElems[0]).asNodeSet(); + for (int j = 0; j < dataElems.size(); j++) { + initializeData(dataElems[j]); + } + } + stateElem.setAttribute("isFirstEntry", ""); + } + // execute onentry executable content + Arabica::XPath::NodeSet<std::string> onEntryElems = _xpath.evaluate("" + _nsPrefix + "onentry", stateElem).asNodeSet(); + for (int j = 0; j < onEntryElems.size(); j++) { + executeContent(onEntryElems[j]); + } + if (isMember(stateElem, statesForDefaultEntry)) { + // execute initial transition content for compund states + Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _nsPrefix + "initial/" + _nsPrefix + "transition", stateElem).asNodeSet(); + for (int j = 0; j < transitions.size(); j++) { + executeContent(transitions[j]); + } + } + + if (isFinal(stateElem)) { + Arabica::DOM::Element<std::string> parent = (Arabica::DOM::Element<std::string>)stateElem.getParentNode(); + + Event event; + event.name = "done.state." + parent.getAttribute("id"); + Arabica::XPath::NodeSet<std::string> doneData = _xpath.evaluate("" + _nsPrefix + "donedata", stateElem).asNodeSet(); + if (doneData.size() > 0) { + event.dom = doneData[0]; + } + _internalQueue.push_back(event); + + if (isParallel(parent.getParentNode())) { + Arabica::DOM::Element<std::string> grandParent = (Arabica::DOM::Element<std::string>)parent.getParentNode(); + + Arabica::XPath::NodeSet<std::string> childs = getChildStates(grandParent); + bool inFinalState = true; + for (int j = 0; j < childs.size(); j++) { + if (!isInFinalState(childs[j])) { + inFinalState = false; + break; + } + } + if (inFinalState) { + Event event; + event.name = "done.state." + grandParent.getAttribute("id"); + _internalQueue.push_back(event); + } + } + } + } + for (int i = 0; i < _configuration.size(); i++) { + Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)_configuration[i]; + if (isFinal(stateElem) && parentIsScxmlState(stateElem)) + _running = false; + } +} + +bool Interpreter::parentIsScxmlState(Arabica::DOM::Node<std::string> state) { + Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)state; + Arabica::DOM::Element<std::string> parentElem = (Arabica::DOM::Element<std::string>)state.getParentNode(); + if (boost::iequals(parentElem.getTagName(), "scxml")) + return true; + return false; +} + +bool Interpreter::isInFinalState(const Arabica::DOM::Node<std::string>& state) { + if (isCompound(state)) { + Arabica::XPath::NodeSet<std::string> childs = getChildStates(state); + for (int i = 0; i < childs.size(); i++) { + if (isFinal(childs[i]) && isMember(childs[i], _configuration)) + return true; + } + } else if (isParallel(state)) { + Arabica::XPath::NodeSet<std::string> childs = getChildStates(state); + for (int i = 0; i < childs.size(); i++) { + if (!isInFinalState(childs[i])) + return false; + } + return true; + } + return false; +} + +bool Interpreter::isMember(const Arabica::DOM::Node<std::string>& node, const Arabica::XPath::NodeSet<std::string>& set) { + for (int i = 0; i < set.size(); i++) { + if (set[i] == node) + return true; + } + return false; +} + +void Interpreter::addStatesToEnter(const Arabica::DOM::Node<std::string>& state, + Arabica::XPath::NodeSet<std::string>& statesToEnter, + Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { + std::string stateId = ((Arabica::DOM::Element<std::string>)state).getAttribute("id"); + if (isHistory(state)) { + if (_historyValue.find(stateId) != _historyValue.end()) { + Arabica::XPath::NodeSet<std::string> historyValue = _historyValue[stateId]; + for (int i = 0; i < historyValue.size(); i++) { + addStatesToEnter(historyValue[i], statesToEnter, statesForDefaultEntry); + NodeSet<std::string> ancestors = getProperAncestors(historyValue[i], state); + for (int j = 0; j < ancestors.size(); j++) { + statesToEnter.push_back(ancestors[j]); + } + } + } else { + NodeSet<std::string> transitions = _xpath.evaluate("" + _nsPrefix + "transition", state).asNodeSet(); + for (int i = 0; i < transitions.size(); i++) { + NodeSet<std::string> targets = getTargetStates(transitions[i]); + for (int j = 0; j < targets.size(); j++) { + addStatesToEnter(targets[j], statesToEnter, statesForDefaultEntry); + } + } + } + } else { + statesToEnter.push_back(state); + if (isCompound(state)) { + statesForDefaultEntry.push_back(state); + NodeSet<std::string> tStates = getTargetStates(getInitialState(state)); + for (int i = 0; i < tStates.size(); i++) { + addStatesToEnter(tStates[i], statesToEnter, statesForDefaultEntry); + } + } else if(isParallel(state)) { + NodeSet<std::string> childStates = getChildStates(state); + for (int i = 0; i < childStates.size(); i++) { + addStatesToEnter(childStates[i], statesToEnter, statesForDefaultEntry); + } + } + } +} + +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(); + + childs.insert(childs.begin(), stateChilds.begin(), stateChilds.end()); + childs.insert(childs.begin(), parallelChilds.begin(), parallelChilds.end()); + + return childs; +} + +Arabica::DOM::Node<std::string> Interpreter::findLCCA(const Arabica::XPath::NodeSet<std::string>& states) { +// std::cout << "findLCCA: "; +// for (int i = 0; i < states.size(); i++) { +// std::cout << ((Arabica::DOM::Element<std::string>)states[i]).getAttribute("id") << " - " << states[i].getLocalName() << ", "; +// } +// std::cout << std::flush; + + Arabica::XPath::NodeSet<std::string> ancestors = getProperAncestors(states[0], Arabica::DOM::Node<std::string>()); + ancestors.push_back(states[0]); // state[0] may already be the ancestor - bug in W3C spec? + Arabica::DOM::Node<std::string> ancestor; + for (int i = 0; i < ancestors.size(); i++) { + for (int j = 0; j < states.size(); j++) { +// std::cout << "Checking " << TAGNAME(state) << " and " << TAGNAME(ancestors[i]) << std::endl; + if (!isDescendant(states[j], ancestors[i]) && (states[j] != ancestors[i])) + goto NEXT_ANCESTOR; + } + ancestor = ancestors[i]; + break; + NEXT_ANCESTOR:; + } + assert(ancestor); +// std::cout << " -> " << ((Arabica::DOM::Element<std::string>)ancestor).getAttribute("id") << " " << ancestor.getLocalName() << std::endl; + return ancestor; +} + +Arabica::DOM::Node<std::string> Interpreter::getState(const std::string& stateId) { + // first try atomic and compund states + NodeSet<std::string> target = _xpath.evaluate("//" + _nsPrefix + "state[@id='" + stateId + "']", _doc).asNodeSet(); + if (target.size() > 0) + goto FOUND; + + // now parallel states + target = _xpath.evaluate("//" + _nsPrefix + "parallel[@id='" + stateId + "']", _doc).asNodeSet(); + if (target.size() > 0) + goto FOUND; + + // now final states + target = _xpath.evaluate("//" + _nsPrefix + "final[@id='" + stateId + "']", _doc).asNodeSet(); + if (target.size() > 0) + goto FOUND; + +FOUND: + if (target.size() > 0) { + assert(target.size() == 1); + return target[0]; + } + // return the empty node + return Arabica::DOM::Node<std::string>(); +} + +Arabica::DOM::Node<std::string> Interpreter::getSourceState(const Arabica::DOM::Node<std::string>& transition) { + if (boost::iequals(TAGNAME(transition.getParentNode()), "initial")) + return transition.getParentNode().getParentNode(); + return transition.getParentNode(); +} + + /** + * In a conformant SCXML document, a compound state may specify either an "initial" + * attribute or an <initial> element, but not both. See 3.6 <initial> for a + * discussion of the difference between the two notations. If neither the "initial" + * attribute nor an <initial> element is specified, the SCXML Processor must use + * the first child state in document order as the default initial state. + */ +Arabica::DOM::Node<std::string> Interpreter::getInitialState(Arabica::DOM::Node<std::string> state) { + if (!state) { + state = _doc.getFirstChild(); + while(!isState(state)) + state = state.getNextSibling(); + } + + assert(isCompound(state) || isParallel(state)); + + // initial attribute at element + Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)state; + if (stateElem.hasAttribute("initial")) { + return getState(stateElem.getAttribute("initial")); + } + + // initial element as child + NodeSet<std::string> initialStates = _xpath.evaluate("" + _nsPrefix + "initial", state).asNodeSet(); + if(initialStates.size() == 1) + return initialStates[0]; + + // first child state + NodeList<std::string> childs = state.getChildNodes(); + for (int i = 0; i < childs.getLength(); i++) { + if (isState(childs.item(i))) + return childs.item(i); + } + // nothing found + return Arabica::DOM::Node<std::string>(); +} + +NodeSet<std::string> Interpreter::getTargetStates(const Arabica::DOM::Node<std::string>& transition) { + NodeSet<std::string> targetStates; + std::string targetId = ((Arabica::DOM::Element<std::string>)transition).getAttribute("target"); + + std::vector<std::string> targetIds = Interpreter::tokenizeIdRefs(ATTR(transition, "target")); + for (int i = 0; i < targetIds.size(); i++) { + targetStates.push_back(getState(targetIds[i])); + } + return targetStates; +} + +std::vector<std::string> Interpreter::tokenizeIdRefs(const std::string& idRefs) { + std::vector<std::string> ids; + + if (idRefs.length() > 0) { + std::istringstream iss(idRefs); + + std::copy(std::istream_iterator<std::string>(iss), + std::istream_iterator<std::string>(), + std::back_inserter<std::vector<std::string> >(ids)); + } + + return ids; +} + +NodeSet<std::string> Interpreter::getProperAncestors(const Arabica::DOM::Node<std::string>& s1, + const Arabica::DOM::Node<std::string>& s2) { + NodeSet<std::string> ancestors; + if (isState(s1)) { + Arabica::DOM::Node<std::string> node = s1; + while((node = node.getParentNode())) { + if (!isState(node)) + break; + if (!boost::iequals(TAGNAME(node), "parallel") && !boost::iequals(TAGNAME(node), "state") && !boost::iequals(TAGNAME(node), "scxml")) + break; + if (node == s2) + break; + ancestors.push_back(node); + } + } + return ancestors; +} + +bool Interpreter::isDescendant(const Arabica::DOM::Node<std::string>& s1, + const Arabica::DOM::Node<std::string>& s2) { + Arabica::DOM::Node<std::string> parent = s1.getParentNode(); + while(parent) { + if (s2 == parent) + return true; + parent = parent.getParentNode(); + } + return false; +} + +bool Interpreter::isTargetless(const Arabica::DOM::Node<std::string>& transition) { + if (transition.hasAttributes()) { + if (((Arabica::DOM::Element<std::string>)transition).hasAttribute("target")) + return false; + } + return true; +} + +bool Interpreter::isWithinSameChild(const Arabica::DOM::Node<std::string>& transition) { + if (!isTargetless(transition)) { + std::string target = ((Arabica::DOM::Element<std::string>)transition).getAttribute("target"); + Arabica::XPath::XPath<std::string> xpath; + // @todo: do we need to look at parallel as well? + if (xpath.evaluate("" + _nsPrefix + "state[id=\"" + target + "\"]", transition.getParentNode()).asNodeSet().size() > 0) + return true; + } + return false; +} + +bool Interpreter::isState(const Arabica::DOM::Node<std::string>& state) { + if (!state) + return false; + if (state.getNodeType() != Arabica::DOM::Node_base::ELEMENT_NODE) + return false; + + std::string tagName = TAGNAME(state); + if (boost::iequals("state", tagName)) + return true; + if (boost::iequals("scxml", tagName)) + return true; + if (boost::iequals("parallel", tagName)) + return true; + if (boost::iequals("final", tagName)) + return true; + return false; +} + +bool Interpreter::isFinal(const Arabica::DOM::Node<std::string>& state) { + std::string tagName = TAGNAME(state); + if (boost::iequals("final", tagName)) + return true; + if (HAS_ATTR(state, "final") && boost::iequals("true", ATTR(state, "final"))) + return true; + return false; +} + +bool Interpreter::isPseudoState(const Arabica::DOM::Node<std::string>& state) { + std::string tagName = TAGNAME(state); + if (boost::iequals("initial", tagName)) + return true; + if (boost::iequals("history", tagName)) + return true; + return false; +} + +bool Interpreter::isTransitionTarget(const Arabica::DOM::Node<std::string>& elem) { + return (isState(elem) || boost::iequals(TAGNAME(elem), "history")); +} + +bool Interpreter::isAtomic(const Arabica::DOM::Node<std::string>& state) { + if (boost::iequals("final", TAGNAME(state))) + return true; + + // I will assume that parallel states are not meant to be atomic. + if (boost::iequals("parallel", TAGNAME(state))) + return false; + + Arabica::DOM::NodeList<std::string> childs = state.getChildNodes(); + for (unsigned int i = 0; i < childs.getLength(); i++) { + if (isState(childs.item(i))) + return false; + } + return true; +} + +bool Interpreter::isHistory(const Arabica::DOM::Node<std::string>& state) { + if (boost::iequals("history", TAGNAME(state))) + return true; + return false; +} + +bool Interpreter::isParallel(const Arabica::DOM::Node<std::string>& state) { + if (!isState(state)) + return false; + if (boost::iequals("parallel", TAGNAME(state))) + return true; + return false; +} + + +bool Interpreter::isCompound(const Arabica::DOM::Node<std::string>& state) { + if (!isState(state)) + return false; + + if (boost::iequals(TAGNAME(state), "parallel")) + return false; + + Arabica::DOM::NodeList<std::string> childs = state.getChildNodes(); + for (unsigned int i = 0; i < childs.getLength(); i++) { + if (isState(childs.item(i))) + return true; + } + return false; +} + +void Interpreter::setupIOProcessors() { + std::map<std::string, IOProcessor*>::iterator ioProcIter = Factory::getInstance()->_ioProcessors.begin(); + while(ioProcIter != Factory::getInstance()->_ioProcessors.end()) { + _ioProcessors[ioProcIter->first] = Factory::getIOProcessor(ioProcIter->first, this); + if (_dataModel) { + try { + _dataModel->assign("_ioprocessors['" + ioProcIter->first + "']", "'" + _ioProcessors[ioProcIter->first]->getURL() + "'"); + } catch (Event e) { + LOG(ERROR) << "Syntax error when setting _ioprocessors:" << std::endl << e << std::endl; + } + } else { + LOG(INFO) << "Not registering " << ioProcIter->first << " at _ioprocessors in datamodel, no datamodel specified"; + } + ioProcIter++; + } +} + +IOProcessor* Interpreter::getIOProcessor(const std::string& type) { + if (_ioProcessors.find(type) == _ioProcessors.end()) { + LOG(ERROR) << "No ioProcessor known for type " << type; + return NULL; + } + return _ioProcessors[type]; +} + +IOProcessor* Interpreter::getIOProcessorForId(const std::string& sendId) { + if (_ioProcessorsIds.find(sendId) == _ioProcessorsIds.end()) { + LOG(ERROR) << "No ioProcessor with pending send id " << sendId << sendId; + return NULL; + } + return _ioProcessorsIds[sendId]; +} + +bool Interpreter::validate() { + bool validationErrors = false; + + if (!_doc) { + LOG(ERROR) << "Document " << _url << " was not parsed successfully" << std::endl; + return false; + } + + // semantic issues ------------ + if ((_xpath.evaluate("/" + _nsPrefix + "scxml/@datamodel", _doc).asNodeSet().size() == 0) && + _xpath.evaluate("/" + _nsPrefix + "scxml/" + _nsPrefix + "script", _doc).asNodeSet().size() > 0) { + LOG(ERROR) << "Script elements used, but no datamodel specified" << std::endl; + } + + // element issues ------------ + Arabica::XPath::NodeSet<std::string> scxmlElems = _xpath.evaluate(_nsPrefix + "scxml", _doc).asNodeSet(); + if (scxmlElems.size() > 0) + LOG(ERROR) << "More than one scxml element found" << std::endl; + for (unsigned int i = 0; i < scxmlElems.size(); i++) { + if (!HAS_ATTR(scxmlElems[i], "xmlns")) + LOG(ERROR) << "scxml element has no xmlns attribute" << std::endl; + if (!HAS_ATTR(scxmlElems[i], "version")) + LOG(ERROR) << "scxml element has no version attribute" << std::endl; + NodeList<std::string> childs = scxmlElems[i].getChildNodes(); + for (unsigned int j = 0; j < childs.getLength(); j++) { + if (childs.item(j).getNodeType() != Node_base::ELEMENT_NODE) + continue; + if (boost::iequals(TAGNAME(childs.item(j)), "state") || + boost::iequals(TAGNAME(childs.item(j)), "parallel") || + boost::iequals(TAGNAME(childs.item(j)), "final") || + boost::iequals(TAGNAME(childs.item(j)), "datamodel") || + boost::iequals(TAGNAME(childs.item(j)), "script")) { + LOG(ERROR) << "scxml element has unspecified child " << TAGNAME(childs.item(j)) << std::endl; + } + } + } + + Arabica::XPath::NodeSet<std::string> stateElems = _xpath.evaluate(_nsPrefix + "state", _doc).asNodeSet(); + for (unsigned int i = 0; i < stateElems.size(); i++) { + NodeList<std::string> childs = stateElems[i].getChildNodes(); + for (unsigned int j = 0; j < childs.getLength(); j++) { + if (childs.item(j).getNodeType() != Node_base::ELEMENT_NODE) + continue; + if (boost::iequals(TAGNAME(childs.item(j)), "onentry") || + boost::iequals(TAGNAME(childs.item(j)), "onexit") || + boost::iequals(TAGNAME(childs.item(j)), "transition") || + boost::iequals(TAGNAME(childs.item(j)), "initial") || + boost::iequals(TAGNAME(childs.item(j)), "state") || + boost::iequals(TAGNAME(childs.item(j)), "parallel") || + boost::iequals(TAGNAME(childs.item(j)), "final") || + boost::iequals(TAGNAME(childs.item(j)), "history") || + boost::iequals(TAGNAME(childs.item(j)), "datamodel") || + boost::iequals(TAGNAME(childs.item(j)), "invoke")) { + LOG(ERROR) << "state element has unspecified child " << TAGNAME(childs.item(j)) << std::endl; + } + } + } + + Arabica::XPath::NodeSet<std::string> parallelElems = _xpath.evaluate(_nsPrefix + "parallel", _doc).asNodeSet(); + for (unsigned int i = 0; i < parallelElems.size(); i++) { + NodeList<std::string> childs = parallelElems[i].getChildNodes(); + for (unsigned int j = 0; j < childs.getLength(); j++) { + if (childs.item(j).getNodeType() != Node_base::ELEMENT_NODE) + continue; + if (boost::iequals(TAGNAME(childs.item(j)), "onentry") || + boost::iequals(TAGNAME(childs.item(j)), "onexit") || + boost::iequals(TAGNAME(childs.item(j)), "transition") || + boost::iequals(TAGNAME(childs.item(j)), "state") || + boost::iequals(TAGNAME(childs.item(j)), "parallel") || + boost::iequals(TAGNAME(childs.item(j)), "history") || + boost::iequals(TAGNAME(childs.item(j)), "datamodel") || + boost::iequals(TAGNAME(childs.item(j)), "invoke")) { + LOG(ERROR) << "parallel element has unspecified child " << TAGNAME(childs.item(j)) << std::endl; + } + } + } + + Arabica::XPath::NodeSet<std::string> transitionElems = _xpath.evaluate(_nsPrefix + "transition", _doc).asNodeSet(); + for (unsigned int i = 0; i < transitionElems.size(); i++) { + if (HAS_ATTR(transitionElems[i], "cond") && + !HAS_ATTR(transitionElems[i], "event")) { + LOG(ERROR) << "transition element with cond attribute but without event attribute not allowed" << std::endl; + } + NodeList<std::string> childs = scxmlElems[i].getChildNodes(); + for (unsigned int j = 0; j < childs.getLength(); j++) { + if (childs.item(j).getNodeType() != Node_base::ELEMENT_NODE) + continue; + } + } + + Arabica::XPath::NodeSet<std::string> initialElems = _xpath.evaluate(_nsPrefix + "initial", _doc).asNodeSet(); + for (unsigned int i = 0; i < initialElems.size(); i++) { + NodeList<std::string> childs = initialElems[i].getChildNodes(); + for (unsigned int j = 0; j < childs.getLength(); j++) { + if (childs.item(j).getNodeType() != Node_base::ELEMENT_NODE) + continue; + if (boost::iequals(TAGNAME(childs.item(j)), "transition")) { + LOG(ERROR) << "initial element has unspecified child " << TAGNAME(childs.item(j)) << std::endl; + } + } + } + + Arabica::XPath::NodeSet<std::string> finalElems = _xpath.evaluate(_nsPrefix + "final", _doc).asNodeSet(); + for (unsigned int i = 0; i < finalElems.size(); i++) { + NodeList<std::string> childs = finalElems[i].getChildNodes(); + for (unsigned int j = 0; j < childs.getLength(); j++) { + if (childs.item(j).getNodeType() != Node_base::ELEMENT_NODE) + continue; + if (!boost::iequals(TAGNAME(childs.item(j)), "onentry") || + !boost::iequals(TAGNAME(childs.item(j)), "onexit") || + !boost::iequals(TAGNAME(childs.item(j)), "donedata")) { + LOG(ERROR) << "parallel element has unspecified child " << TAGNAME(childs.item(j)) << std::endl; + } + } + } + + Arabica::XPath::NodeSet<std::string> historyElems = _xpath.evaluate(_nsPrefix + "history", _doc).asNodeSet(); + for (unsigned int i = 0; i < historyElems.size(); i++) { + NodeList<std::string> childs = historyElems[i].getChildNodes(); + for (unsigned int j = 0; j < childs.getLength(); j++) { + if (childs.item(j).getNodeType() != Node_base::ELEMENT_NODE) + continue; + if (boost::iequals(TAGNAME(childs.item(j)), "transition")) { + LOG(ERROR) << "history element has unspecified child " << TAGNAME(childs.item(j)) << std::endl; + } + } + } + + Arabica::XPath::NodeSet<std::string> datamodelElems = _xpath.evaluate(_nsPrefix + "datamodel", _doc).asNodeSet(); + for (unsigned int i = 0; i < datamodelElems.size(); i++) { + NodeList<std::string> childs = datamodelElems[i].getChildNodes(); + for (unsigned int j = 0; j < childs.getLength(); j++) { + if (childs.item(j).getNodeType() != Node_base::ELEMENT_NODE) + continue; + if (!boost::iequals(TAGNAME(childs.item(j)), "data")) { + LOG(ERROR) << "datamodel element has unspecified child " << TAGNAME(childs.item(j)) << std::endl; + } + } + } + + Arabica::XPath::NodeSet<std::string> dataElems = _xpath.evaluate(_nsPrefix + "data", _doc).asNodeSet(); + for (unsigned int i = 0; i < dataElems.size(); i++) { + if (!HAS_ATTR(dataElems[i], "id")) + LOG(ERROR) << "data element has no id attribute" << std::endl; + } + + return validationErrors; +} + +void Interpreter::dump() { + if (!_doc) + return; + dump(_doc); +} + +void Interpreter::dump(const Arabica::DOM::Node<std::string>& node, int lvl) { + if (!node) + return; + + std::string indent = ""; + for (unsigned int i = 0; i < lvl; i++) + indent += " "; + + std::cout << indent; + switch(node.getNodeType()) { + case Arabica::DOM::Node_base::ELEMENT_NODE: { + std::cout << "ELEMENT_NODE: "; + Arabica::DOM::Element<std::string> elem = (Arabica::DOM::Element<std::string>)node; + break; + } + case Arabica::DOM::Node_base::ATTRIBUTE_NODE: + std::cout << "ATTRIBUTE_NODE: "; + break; + case Arabica::DOM::Node_base::TEXT_NODE: + std::cout << "TEXT_NODE: "; + break; + case Arabica::DOM::Node_base::CDATA_SECTION_NODE: + std::cout << "CDATA_SECTION_NODE: "; + break; + case Arabica::DOM::Node_base::ENTITY_REFERENCE_NODE: + std::cout << "ENTITY_REFERENCE_NODE: "; + break; + case Arabica::DOM::Node_base::ENTITY_NODE: + std::cout << "ENTITY_NODE: "; + break; + case Arabica::DOM::Node_base::PROCESSING_INSTRUCTION_NODE: + std::cout << "PROCESSING_INSTRUCTION_NODE: "; + break; + case Arabica::DOM::Node_base::COMMENT_NODE: + std::cout << "COMMENT_NODE: "; + break; + case Arabica::DOM::Node_base::DOCUMENT_NODE: + std::cout << "DOCUMENT_NODE: "; + break; + case Arabica::DOM::Node_base::DOCUMENT_TYPE_NODE: + std::cout << "DOCUMENT_TYPE_NODE: "; + break; + case Arabica::DOM::Node_base::DOCUMENT_FRAGMENT_NODE: + std::cout << "DOCUMENT_FRAGMENT_NODE: "; + break; + case Arabica::DOM::Node_base::NOTATION_NODE: + std::cout << "NOTATION_NODE: "; + break; + case Arabica::DOM::Node_base::MAX_TYPE: + std::cout << "MAX_TYPE: "; + break; + } + std::cout << node.getNamespaceURI() << " " << node.getNodeName() << std::endl; + + if (node.getNodeValue().length() > 0 && node.getNodeValue().find_first_not_of(" \t\n") != std::string::npos) + std::cout << indent << "Value: '" << node.getNodeValue() << "'" << std::endl; + + + if (node.hasAttributes()) { + Arabica::DOM::NamedNodeMap<std::string> attrs = node.getAttributes(); + for (unsigned int i = 0; i < attrs.getLength(); i++) { + std::cout << indent << " " << attrs.item(i).getLocalName() << " = " << attrs.item(i).getNodeValue() << " (" << std::endl; + std::cout << indent << " namespace: " << attrs.item(i).getNamespaceURI() << std::endl; + std::cout << indent << " nodeName: " << attrs.item(i).getNodeName() << std::endl; + std::cout << indent << " prefix: " << attrs.item(i).getPrefix() << std::endl; + std::cout << indent << " )" << std::endl; + } + } + + if (node.hasChildNodes()) { + Arabica::DOM::NodeList<std::string> childs = node.getChildNodes(); + for (unsigned int i = 0; i < childs.getLength(); i++) { + dump(childs.item(i), lvl+1); + } + } +} + +}
\ No newline at end of file diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h new file mode 100644 index 0000000..fb913d1 --- /dev/null +++ b/src/uscxml/Interpreter.h @@ -0,0 +1,314 @@ +#ifndef RUNTIME_H_SQ1MBKGN +#define RUNTIME_H_SQ1MBKGN + +#include <boost/uuid/uuid_generators.hpp> +#include <boost/algorithm/string.hpp> + +#include <iostream> +#include <set> +#include <map> + +#include <XPath/XPath.hpp> +#include <DOM/Document.hpp> + +#include <DOM/SAX2DOM/SAX2DOM.hpp> +#include <SAX/helpers/CatchErrorHandler.hpp> + +#include "uscxml/concurrency/tinythread.h" +#include "uscxml/concurrency/BlockingQueue.h" +#include "uscxml/Message.h" +#include "uscxml/Factory.h" + +namespace uscxml { + + class Interpreter { + public: + enum Binding { + EARLY = 0, + LATE = 1 + }; + + Interpreter(const std::string& url); + virtual ~Interpreter(); + + void start(); + void stop(); + static void run(void*); + + void interpret(); + bool validate(); + + void waitForStabilization(); + + void receive(Event& event) { _externalQueue.push(event); } + 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); + + const std::string& getName() { return _name; } + const std::string& getSessionId() { return _sessionId; } + + static bool isMember(const Arabica::DOM::Node<std::string>& node, const Arabica::XPath::NodeSet<std::string>& set); + + void dump(); + static void dump(const Arabica::DOM::Node<std::string>& node, int lvl = 0); + + protected: + void normalize(const Arabica::DOM::Node<std::string>& node); + void setupIOProcessors(); + + void mainEventLoop(); + + bool _stable; + tthread::thread* _thread; + tthread::mutex _mutex; + tthread::condition_variable _stabilized; + + std::string _url; + Arabica::DOM::Document<std::string> _doc; + Arabica::DOM::Element<std::string> _scxml; + Arabica::XPath::XPath<std::string> _xpath; + Arabica::XPath::StandardNamespaceContext<std::string> _nsContext; + std::string _nsPrefix; + + bool _running; + Binding _binding; + Arabica::XPath::NodeSet<std::string> _configuration; + Arabica::XPath::NodeSet<std::string> _statesToInvoke; + + DataModel* _dataModel; + std::map<std::string, Arabica::XPath::NodeSet<std::string> > _historyValue; + + std::list<Event > _internalQueue; + uscxml::concurrency::BlockingQueue<Event> _externalQueue; + + void microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + void executeTransitionContent(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + void executeContent(const Arabica::DOM::Node<std::string>& content); + void executeContent(const Arabica::DOM::NodeList<std::string>& content); + void initializeData(const Arabica::DOM::Node<std::string>& data); + void exitInterpreter(); + + void addStatesToEnter(const Arabica::DOM::Node<std::string>& state, + Arabica::XPath::NodeSet<std::string>& statesToEnter, + Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); + + 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); + + + void send(const std::string invokeId, Event& event); + void send(const Arabica::DOM::Node<std::string>& element); + void invoke(const Arabica::DOM::Node<std::string>& element); + void cancelInvoke(const Arabica::DOM::Node<std::string>& content); + void returnDoneEvent(const Arabica::DOM::Node<std::string>& state); + + static bool nameMatch(const std::string& transitionEvent, const std::string& event); + Arabica::XPath::NodeSet<std::string> filterPreempted(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + bool hasConditionMatch(const Arabica::DOM::Node<std::string>& conditional); + bool isPreemptingTransition(const Arabica::DOM::Node<std::string>& t1, const Arabica::DOM::Node<std::string>& t2); + 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; + static const std::string getUUID(); + + std::string _name; + std::string _sessionId; + + IOProcessor* getIOProcessor(const std::string& type); + IOProcessor* getIOProcessorForId(const std::string& sendId); + + std::map<std::string, IOProcessor*> _ioProcessorsIds; + std::map<std::string, IOProcessor*> _ioProcessors; + + }; + +#if 0 + class SCXMLParseHandler : + public Arabica::SAX::EntityResolver<std::string>, + public Arabica::SAX::DTDHandler<std::string>, + public Arabica::SAX::ContentHandler<std::string>, + public Arabica::SAX::CatchErrorHandler<std::string>, + public Arabica::SAX::LexicalHandler<std::string>, + public Arabica::SAX::DeclHandler<std::string> + { + public: + SCXMLParseHandler() { } + virtual ~SCXMLParseHandler() { } + + // EntityResolver + virtual InputSourceT resolveEntity(const std::string& publicId , const std::string& systemId) { + return InputSourceT(); + } + + // DTDHandler + virtual void notationDecl(const std::string& name, + const std::string& publicId, + const std::string& systemId) { + std::cout << "notationDecl" << std::endl; + std::cout << " name:" << name << std::endl; + std::cout << " publicId:" << publicId << std::endl; + std::cout << " systemId:" << systemId << std::endl; + } + virtual void unparsedEntityDecl(const std::string& name, + const std::string& publicId, + const std::string& systemId, + const std::string& notationName) { + std::cout << "unparsedEntityDecl" << std::endl; + std::cout << " name:" << name << std::endl; + std::cout << " publicId:" << publicId << std::endl; + std::cout << " systemId:" << systemId << std::endl; + std::cout << " notationName:" << notationName << std::endl; + } + + // ContentHandler + virtual void setDocumentLocator(const LocatorT& locator) { + std::cout << "setDocumentLocator" << std::endl; + } + virtual void startDocument() { + std::cout << "startDocument" << std::endl; + } + virtual void endDocument() { + std::cout << "endDocument" << std::endl; + } + virtual void startPrefixMapping(const std::string& prefix, const std::string& uri) { + std::cout << "startPrefixMapping" << std::endl; + std::cout << " prefix:" << prefix << std::endl; + std::cout << " uri:" << uri << std::endl; + } + virtual void endPrefixMapping(const std::string& prefix) { + std::cout << "endPrefixMapping" << std::endl; + std::cout << " prefix:" << prefix << std::endl; + } + virtual void startElement(const std::string& namespaceURI, const std::string& localName, + const std::string& qName, const AttributesT& atts) { + std::cout << "startElement" << std::endl; + std::cout << " namespaceURI:" << namespaceURI << std::endl; + std::cout << " localName:" << localName << std::endl; + std::cout << " qName:" << qName << std::endl; + std::cout << " atts:" << atts.getLength() << std::endl; + } + virtual void endElement(const std::string& namespaceURI, const std::string& localName, + const std::string& qName) { + std::cout << "endElement" << std::endl; + std::cout << " namespaceURI:" << namespaceURI << std::endl; + std::cout << " localName:" << localName << std::endl; + std::cout << " qName:" << qName << std::endl; + } + virtual void characters(const std::string& ch) { + std::cout << "characters" << std::endl; + std::cout << " ch:" << ch << std::endl; + } + virtual void ignorableWhitespace(const std::string& ch) { + std::cout << "ignorableWhitespace" << std::endl; + std::cout << " ch:" << ch << std::endl; + } + virtual void processingInstruction(const std::string& target, const std::string& data) { + std::cout << "processingInstruction" << std::endl; + std::cout << " target:" << target << std::endl; + std::cout << " data:" << data << std::endl; + } + virtual void skippedEntity(const std::string& name) { + std::cout << "skippedEntity" << std::endl; + std::cout << " name:" << name << std::endl; + } + + // ErrorHandler + virtual void warning(const SAXParseExceptionT& e) { Arabica::SAX::CatchErrorHandler<std::string>::warning(e); } + virtual void error(const SAXParseExceptionT& e) { Arabica::SAX::CatchErrorHandler<std::string>::error(e); } + virtual void fatalError(const SAXParseExceptionT& e) { + Arabica::SAX::CatchErrorHandler<std::string>::fatalError(e); + } + + // LexicalHandler + virtual void startDTD(const std::string& name, + const std::string& publicId, + const std::string& systemId) { + std::cout << "startDTD" << std::endl; + std::cout << " name:" << name << std::endl; + std::cout << " publicId:" << publicId << std::endl; + std::cout << " systemId:" << systemId << std::endl; + } + + virtual void endDTD() { + std::cout << "endDTD" << std::endl; + } + virtual void startEntity(const std::string& name) { + std::cout << "startEntity" << std::endl; + std::cout << " name:" << name << std::endl; + } + virtual void endEntity(const std::string& name) { + std::cout << "endEntity" << std::endl; + std::cout << " name:" << name << std::endl; + } + virtual void startCDATA() { + std::cout << "startCDATA" << std::endl; + } + virtual void endCDATA() { + std::cout << "endCDATA" << std::endl; + } + virtual void comment(const std::string& text) { + std::cout << "comment" << std::endl; + std::cout << " text:" << text << std::endl; + } + + // DeclHandler + virtual void elementDecl(const std::string& name, const std::string& model) { + std::cout << "elementDecl" << std::endl; + std::cout << " name:" << name << std::endl; + std::cout << " model:" << model << std::endl; + } + virtual void attributeDecl(const std::string& elementName, + const std::string& attributeName, + const std::string& type, + const std::string& valueDefault, + const std::string& value) { + std::cout << "attributeDecl" << std::endl; + std::cout << " elementName:" << elementName << std::endl; + std::cout << " attributeName:" << attributeName << std::endl; + std::cout << " type:" << type << std::endl; + std::cout << " valueDefault:" << valueDefault << std::endl; + std::cout << " value:" << value << std::endl; + } + virtual void internalEntityDecl(const std::string& name, const std::string& value) { + std::cout << "internalEntityDecl" << std::endl; + std::cout << " name:" << name << std::endl; + std::cout << " value:" << value << std::endl; + } + virtual void externalEntityDecl(const std::string& name, + const std::string& publicId, + const std::string& systemId) { + std::cout << "externalEntityDecl" << std::endl; + std::cout << " name:" << name << std::endl; + std::cout << " publicId:" << publicId << std::endl; + std::cout << " systemId:" << systemId << std::endl; + } + + }; +#endif +} + +#endif diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp new file mode 100644 index 0000000..3d3048b --- /dev/null +++ b/src/uscxml/Message.cpp @@ -0,0 +1,168 @@ +#include "uscxml/Message.h" +#include <DOM/SAX2DOM/SAX2DOM.hpp> +#include <SAX/helpers/CatchErrorHandler.hpp> + +namespace uscxml { + +static int _dataIndentation = 1; + + +Arabica::DOM::Document<std::string> Data::toDocument() { + Arabica::DOM::DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); + Arabica::DOM::Document<std::string> document = domFactory.createDocument("http://www.w3.org/2005/07/scxml", "message", 0); + Arabica::DOM::Element<std::string> scxmlMsg = document.getDocumentElement(); + scxmlMsg.setPrefix("scxml"); + scxmlMsg.setAttribute("version", "1.0"); + + if (compound.size() > 0 || array.size() > 0) { + Arabica::DOM::Element<std::string> payloadElem = document.createElementNS("http://www.w3.org/2005/07/scxml", "scxml:payload"); + scxmlMsg.appendChild(payloadElem); + + // we do not support nested attibutes + if (compound.size() > 0) { + std::map<std::string, Data>::iterator compoundIter = compound.begin(); + while(compoundIter != compound.end()) { + if (compoundIter->second.atom.size() > 0) { + Arabica::DOM::Element<std::string> propertyElem = document.createElementNS("http://www.w3.org/2005/07/scxml", "scxml:property"); + propertyElem.setAttribute("name", compoundIter->first); + Arabica::DOM::Text<std::string> textElem = document.createTextNode(compoundIter->second.atom); + propertyElem.appendChild(textElem); + payloadElem.appendChild(propertyElem); + } + compoundIter++; + } + } + } + return document; +} + +Arabica::DOM::Document<std::string> Event::toDocument() { + Arabica::DOM::DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); + Arabica::DOM::Document<std::string> document = Data::toDocument(); + Arabica::DOM::Element<std::string> scxmlMsg = document.getDocumentElement(); + + scxmlMsg.setAttribute("source", origin); + scxmlMsg.setAttribute("name", name); + + return document; +} + +Arabica::DOM::Document<std::string> SendRequest::toDocument() { + Arabica::DOM::DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); + Arabica::DOM::Document<std::string> document = Event::toDocument(); + Arabica::DOM::Element<std::string> scxmlMsg = document.getDocumentElement(); + + scxmlMsg.setAttribute("sendid", sendid); + + return document; +} + +Arabica::DOM::Document<std::string> InvokeRequest::toDocument() { + Arabica::DOM::DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); + Arabica::DOM::Document<std::string> document = Event::toDocument(); + Arabica::DOM::Element<std::string> scxmlMsg = document.getDocumentElement(); + + scxmlMsg.setAttribute("invokeid", invokeid); + + return document; +} + +Data Data::fromXML(const std::string& xmlString) { +} + +Event Event::fromXML(const std::string& xmlString) { + Arabica::SAX2DOM::Parser<std::string> eventParser; + Arabica::SAX::CatchErrorHandler<std::string> errorHandler; + eventParser.setErrorHandler(errorHandler); + + std::istringstream is(xmlString); + Arabica::SAX::InputSource<std::string> inputSource; + inputSource.setByteStream(is); + + Event event; + if(eventParser.parse(inputSource) && eventParser.getDocument().hasChildNodes()) { + Arabica::DOM::Element<std::string> scxmlMsg = eventParser.getDocument().getDocumentElement(); + if (HAS_ATTR(scxmlMsg, "name")) + event.name = ATTR(scxmlMsg, "name"); + if (HAS_ATTR(scxmlMsg, "sendid")) + event.sendid = ATTR(scxmlMsg, "sendid"); + } + return event; +} + +SendRequest SendRequest::fromXML(const std::string& xmlString) { + Event::fromXML(xmlString); +} + +InvokeRequest InvokeRequest::fromXML(const std::string& xmlString) { + Event::fromXML(xmlString); +} + +#ifndef SWIGJAVA +std::ostream& operator<< (std::ostream& os, const Event& event) { + os << (event.type == Event::EXTERNAL ? "External" : "Internal") << " Event " << (event.dom ? "with DOM attached" : "") << std::endl; + + if (event.name.size() > 0) + os << " name: " << event.name << std::endl; + if (event.origin.size() > 0) + os << " origin: " << event.origin << std::endl; + if (event.origintype.size() > 0) + os << " origintype: " << event.origintype << std::endl; + _dataIndentation++; + os << " data: " << (Data)event << std::endl; + _dataIndentation--; + return os; +} +#endif + +#ifndef SWIGJAVA +std::ostream& operator<< (std::ostream& os, const Data& data) { + std::string indent; + for (int i = 0; i < _dataIndentation; i++) { + indent += " "; + } + if (false) { + } else if (data.compound.size() > 0) { + int longestKey = 0; + std::map<std::string, Data>::const_iterator compoundIter = data.compound.begin(); + while(compoundIter != data.compound.end()) { + if (compoundIter->first.size() > longestKey) + longestKey = compoundIter->first.size(); + compoundIter++; + } + std::string keyPadding; + for (unsigned int i = 0; i < longestKey; i++) + keyPadding += " "; + + os << "{" << std::endl; + compoundIter = data.compound.begin(); + while(compoundIter != data.compound.end()) { + os << indent << " \"" << compoundIter->first << "\" " << keyPadding.substr(0, longestKey - compoundIter->first.size()) << "= "; + _dataIndentation += 2; + os << compoundIter->second << "," << std::endl; + _dataIndentation -= 2; + compoundIter++; + } + os << indent << "}" << std::endl; + } else if (data.array.size() > 0) { + os << "[" << std::endl; + std::map<std::string, Data>::const_iterator compoundIter = data.compound.begin(); + while(compoundIter != data.compound.end()) { + _dataIndentation += 2; + os << indent << " " << compoundIter->second << "," << std::endl; + _dataIndentation -= 2; + compoundIter++; + } + os << indent << "]" << std::endl; + } else if (data.atom.size() > 0) { + if (data.type == Data::VERBATIM) { + os << indent << "\"" << data.atom << "\""; + } else { + os << indent << data.atom; + } + } + return os; +} +#endif + +}
\ No newline at end of file diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h new file mode 100644 index 0000000..a520ff5 --- /dev/null +++ b/src/uscxml/Message.h @@ -0,0 +1,122 @@ +#ifndef EVENT_H_XZAQ4HR +#define EVENT_H_XZAQ4HR + +#include <map> +#include <vector> +#include <string> + +#include <DOM/Document.hpp> +#include <DOM/io/Stream.hpp> + +#define TAGNAME(elem) ((Arabica::DOM::Element<std::string>)elem).getTagName() +#define ATTR(elem, attr) ((Arabica::DOM::Element<std::string>)elem).getAttribute(attr) +#define HAS_ATTR(elem, attr) ((Arabica::DOM::Element<std::string>)elem).hasAttribute(attr) + +namespace uscxml { + +class Data { +public: + enum Type { + VERBATIM, + INTERPRETED + }; + + Data() {} + Data(const std::string& atom_, Type type_ = INTERPRETED) : atom(atom_), type(type_) {} + virtual ~Data() {} + + static Data fromXML(const std::string& xmlString); + Arabica::DOM::Document<std::string> toDocument(); + std::string toXMLString() { + std::stringstream ss; + ss << toDocument(); + return ss.str(); + } + + std::map<std::string, Data> compound; + std::vector<Data> array; + std::string atom; + Type type; + +protected: + Arabica::DOM::Document<std::string> toNode(const Arabica::DOM::Document<std::string>& factory, const Data& data); + +#ifndef SWIGJAVA + friend std::ostream& operator<< (std::ostream& os, const Data& data); +#endif +}; + +class Event : public Data { +public: + enum Type { + PLATFORM, + INTERNAL, + EXTERNAL + }; + + Event() : type(INTERNAL) {} + + std::string name; + Type type; + std::string origin; + std::string origintype; + Arabica::DOM::Node<std::string> dom; + std::string sendid; + std::string invokeid; + + static Event fromXML(const std::string& xmlString); + Arabica::DOM::Document<std::string> toDocument(); + std::string toXMLString() { + std::stringstream ss; + ss << toDocument(); + return ss.str(); + } + +#ifndef SWIGJAVA + friend std::ostream& operator<< (std::ostream& os, const Event& event); +#endif +}; + +class InvokeRequest : public Event { +public: + std::string type; + std::string src; + std::string namelist; + bool autoForward; + Arabica::DOM::Node<std::string> finalize; + std::map<std::string, std::string> params; + std::string content; + + static InvokeRequest fromXML(const std::string& xmlString); + Arabica::DOM::Document<std::string> toDocument(); + std::string toXMLString() { + std::stringstream ss; + ss << toDocument(); + return ss.str(); + } + +}; + +class SendRequest : public Event { +public: + std::string target; + std::string type; + uint32_t delayMs; + std::map<std::string, std::string> params; + std::map<std::string, std::string> namelist; + std::string content; + + static SendRequest fromXML(const std::string& xmlString); + Arabica::DOM::Document<std::string> toDocument(); + std::string toXMLString() { + std::stringstream ss; + ss << toDocument(); + return ss.str(); + } + +}; + +} + + +#endif /* end of include guard: EVENT_H_XZAQ4HR */ diff --git a/src/uscxml/concurrency/BlockingQueue.h b/src/uscxml/concurrency/BlockingQueue.h new file mode 100644 index 0000000..90094bf --- /dev/null +++ b/src/uscxml/concurrency/BlockingQueue.h @@ -0,0 +1,41 @@ +#ifndef BLOCKINGQUEUE_H_4LEVMY0N +#define BLOCKINGQUEUE_H_4LEVMY0N + +#include "uscxml/concurrency/tinythread.h" +#include <list> + +namespace uscxml { +namespace concurrency { + +template <class T> +class BlockingQueue { +public: + BlockingQueue() {} + virtual ~BlockingQueue() { + } + + void push(T elem) { + tthread::lock_guard<tthread::mutex> lock(_mutex); + _queue.push_back(elem); + _cond.notify_all(); + } + + T pop() { + tthread::lock_guard<tthread::mutex> lock(_mutex); + while (_queue.empty()) { + _cond.wait(_mutex); + } + T ret = _queue.front(); + _queue.pop_front(); + return ret; + } + + tthread::mutex _mutex; + tthread::condition_variable _cond; + std::list<T> _queue; +}; + +} +} + +#endif /* end of include guard: BLOCKINGQUEUE_H_4LEVMY0N */ diff --git a/src/uscxml/concurrency/eventqueue/libev/DelayedEventQueue.cpp b/src/uscxml/concurrency/eventqueue/libev/DelayedEventQueue.cpp new file mode 100644 index 0000000..a93b14a --- /dev/null +++ b/src/uscxml/concurrency/eventqueue/libev/DelayedEventQueue.cpp @@ -0,0 +1,57 @@ +#include "uscxml/concurrency/DelayedEventQueue.h" +#include <assert.h> + +namespace uscxml { + + DelayedEventQueue::DelayedEventQueue() { + _eventLoop = EV_DEFAULT; + _thread = NULL; + } + + DelayedEventQueue::~DelayedEventQueue() { + ev_break(_eventLoop); + if (_thread) + _thread->join(); + } + + void DelayedEventQueue::addEvent(std::string eventId, void (*callback)(void*, const std::string eventId), uint32_t delayMs, void* userData) { + if(_timeoutWatcher.find(eventId) != _timeoutWatcher.end()) { + cancelEvent(eventId); + } + + _timeoutWatcher[eventId].eventId = eventId; + _timeoutWatcher[eventId].userData = userData; + _timeoutWatcher[eventId].eventQueue = this; + _timeoutWatcher[eventId].callback = callback; + + ev_timer_init (&_timeoutWatcher[eventId].io, DelayedEventQueue::timerCallback, ((float)delayMs)/1000.0f, 0.); + ev_timer_start (_eventLoop, &_timeoutWatcher[eventId].io); + + } + + void DelayedEventQueue::cancelEvent(std::string eventId) { + if(_timeoutWatcher.find(eventId) != _timeoutWatcher.end()) { + ev_timer_stop(_eventLoop, &_timeoutWatcher[eventId].io); + _timeoutWatcher.erase(eventId); + } + } + + void DelayedEventQueue::start() { + _thread = new tthread::thread(DelayedEventQueue::run, this); + } + + void DelayedEventQueue::stop() { + } + + void DelayedEventQueue::run(void* instance) { + ev_run (((DelayedEventQueue*)instance)->_eventLoop, 0); + } + + void DelayedEventQueue::timerCallback(EV_P_ ev_timer *w, int revents) { + struct callbackData *data = (struct callbackData*)w; + std::string eventId = data->eventId; // copy eventId + data->eventQueue->_timeoutWatcher.erase(data->eventId); + data->callback(data->userData, eventId); + } + +}
\ No newline at end of file diff --git a/src/uscxml/concurrency/eventqueue/libev/DelayedEventQueue.h b/src/uscxml/concurrency/eventqueue/libev/DelayedEventQueue.h new file mode 100644 index 0000000..2bc71b2 --- /dev/null +++ b/src/uscxml/concurrency/eventqueue/libev/DelayedEventQueue.h @@ -0,0 +1,45 @@ +#ifndef DELAYEDEVENTQUEUE_H_JA6WRBVP +#define DELAYEDEVENTQUEUE_H_JA6WRBVP + +#include "tinythread.h" +#include <ev.h> + +#include <map> +#include <string> +#include <iostream> + +namespace uscxml { + +class DelayedEventQueue { +public: + + struct callbackData + { + ev_timer io; + void *userData; + void (*callback)(void*, const std::string eventId); + std::string eventId; + DelayedEventQueue* eventQueue; + }; + + DelayedEventQueue(); + virtual ~DelayedEventQueue(); + + void addEvent(std::string eventId, void (*callback)(void*, const std::string eventId), uint32_t delayMs, void* userData); + void cancelEvent(std::string eventId); + + void start(); + void stop(); + static void run(void*); + + static void timerCallback(EV_P_ ev_timer *w, int revents); + + tthread::thread* _thread; + std::map<std::string, callbackData> _timeoutWatcher; + struct ev_loop* _eventLoop; +}; + +} + + +#endif /* end of include guard: DELAYEDEVENTQUEUE_H_JA6WRBVP */ diff --git a/src/uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.cpp b/src/uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.cpp new file mode 100644 index 0000000..ce42af7 --- /dev/null +++ b/src/uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.cpp @@ -0,0 +1,87 @@ +#include "uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.h" +#include <assert.h> +#include <event2/event.h> + +namespace uscxml { + + DelayedEventQueue::DelayedEventQueue() { + evthread_use_pthreads(); + _eventLoop = event_base_new(); + _thread = NULL; + } + + DelayedEventQueue::~DelayedEventQueue() { + std::cout << "Deleting DelayedEventQueue" << std::endl; + if(_eventLoop) + event_base_loopbreak(_eventLoop); + if (_thread) + _thread->join(); + if(_eventLoop) + event_base_free(_eventLoop); + } + + void DelayedEventQueue::run(void* instance) { + DelayedEventQueue* THIS = (DelayedEventQueue*)instance; + int result; + while(THIS->_isStarted) { + { + //result = event_base_dispatch(THIS->_eventLoop); + result = event_base_loop(THIS->_eventLoop, EVLOOP_NO_EXIT_ON_EMPTY); + } + } + } + + void DelayedEventQueue::addEvent(std::string eventId, void (*callback)(void*, const std::string eventId), uint32_t delayMs, void* userData) { + if(_callbackData.find(eventId) != _callbackData.end()) { + cancelEvent(eventId); + } + + struct timeval delay = {delayMs / 1000, (delayMs % 1000) * 1000}; + struct event* event = event_new(_eventLoop, -1, 0, DelayedEventQueue::timerCallback, &_callbackData[eventId]); + + _callbackData[eventId].eventId = eventId; + _callbackData[eventId].userData = userData; + _callbackData[eventId].eventQueue = this; + _callbackData[eventId].callback = callback; + _callbackData[eventId].event = event; + + event_add(event, &delay); + } + + void DelayedEventQueue::cancelEvent(std::string eventId) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + if(_callbackData.find(eventId) != _callbackData.end()) { + event_del(_callbackData[eventId].event); + event_free(_callbackData[eventId].event); + _callbackData.erase(eventId); + } + } + + void DelayedEventQueue::start() { + _isStarted = true; + _thread = new tthread::thread(DelayedEventQueue::run, this); + } + + void DelayedEventQueue::stop() { + if (_isStarted) { + _isStarted = false; + _thread->join(); + delete _thread; + } + } + + void DelayedEventQueue::dummyCallback(evutil_socket_t fd, short what, void *arg) { + } + + void DelayedEventQueue::timerCallback(evutil_socket_t fd, short what, void *arg) { + struct callbackData *data = (struct callbackData*)arg; + tthread::lock_guard<tthread::recursive_mutex> lock(data->eventQueue->_mutex); + + std::string eventId = data->eventId; // copy eventId + event_free(data->event); + data->eventQueue->_callbackData.erase(data->eventId); + data->callback(data->userData, eventId); + } + +}
\ No newline at end of file diff --git a/src/uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.h b/src/uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.h new file mode 100644 index 0000000..4c59ce1 --- /dev/null +++ b/src/uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.h @@ -0,0 +1,52 @@ +#ifndef DELAYEDEVENTQUEUE_H_JA6WRBVP +#define DELAYEDEVENTQUEUE_H_JA6WRBVP + +#include "uscxml/concurrency/tinythread.h" + +#include <event2/thread.h> +#include <event2/http.h> +#include <event2/event.h> + +#include <map> +#include <string> +#include <iostream> + +namespace uscxml { + +class DelayedEventQueue { +public: + + struct callbackData + { + void *userData; + void (*callback)(void*, const std::string eventId); + std::string eventId; + struct event *event; + DelayedEventQueue* eventQueue; + }; + + DelayedEventQueue(); + virtual ~DelayedEventQueue(); + + void addEvent(std::string eventId, void (*callback)(void*, const std::string eventId), uint32_t delayMs, void* userData); + void cancelEvent(std::string eventId); + + void start(); + void stop(); + static void run(void*); + + static void timerCallback(evutil_socket_t fd, short what, void *arg); + static void dummyCallback(evutil_socket_t fd, short what, void *arg); + + bool _isStarted; + tthread::thread* _thread; + tthread::recursive_mutex _mutex; + + std::map<std::string, callbackData> _callbackData; + struct event_base* _eventLoop; +}; + +} + + +#endif /* end of include guard: DELAYEDEVENTQUEUE_H_JA6WRBVP */ diff --git a/src/uscxml/concurrency/tinythread.cpp b/src/uscxml/concurrency/tinythread.cpp new file mode 100644 index 0000000..690ecee --- /dev/null +++ b/src/uscxml/concurrency/tinythread.cpp @@ -0,0 +1,303 @@ +/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- +Copyright (c) 2010-2012 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#include <exception> +#include "tinythread.h" + +#if defined(_TTHREAD_POSIX_) + #include <unistd.h> + #include <map> +#elif defined(_TTHREAD_WIN32_) + #include <process.h> +#endif + + +namespace tthread { + +//------------------------------------------------------------------------------ +// condition_variable +//------------------------------------------------------------------------------ +// NOTE 1: The Win32 implementation of the condition_variable class is based on +// the corresponding implementation in GLFW, which in turn is based on a +// description by Douglas C. Schmidt and Irfan Pyarali: +// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html +// +// NOTE 2: Windows Vista actually has native support for condition variables +// (InitializeConditionVariable, WakeConditionVariable, etc), but we want to +// be portable with pre-Vista Windows versions, so TinyThread++ does not use +// Vista condition variables. +//------------------------------------------------------------------------------ + +#if defined(_TTHREAD_WIN32_) + #define _CONDITION_EVENT_ONE 0 + #define _CONDITION_EVENT_ALL 1 +#endif + +#if defined(_TTHREAD_WIN32_) +condition_variable::condition_variable() : mWaitersCount(0) +{ + mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); + mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); + InitializeCriticalSection(&mWaitersCountLock); +} +#endif + +#if defined(_TTHREAD_WIN32_) +condition_variable::~condition_variable() +{ + CloseHandle(mEvents[_CONDITION_EVENT_ONE]); + CloseHandle(mEvents[_CONDITION_EVENT_ALL]); + DeleteCriticalSection(&mWaitersCountLock); +} +#endif + +#if defined(_TTHREAD_WIN32_) +void condition_variable::_wait() +{ + // Wait for either event to become signaled due to notify_one() or + // notify_all() being called + int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE); + + // Check if we are the last waiter + EnterCriticalSection(&mWaitersCountLock); + -- mWaitersCount; + bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && + (mWaitersCount == 0); + LeaveCriticalSection(&mWaitersCountLock); + + // If we are the last waiter to be notified to stop waiting, reset the event + if(lastWaiter) + ResetEvent(mEvents[_CONDITION_EVENT_ALL]); +} +#endif + +#if defined(_TTHREAD_WIN32_) +void condition_variable::notify_one() +{ + // Are there any waiters? + EnterCriticalSection(&mWaitersCountLock); + bool haveWaiters = (mWaitersCount > 0); + LeaveCriticalSection(&mWaitersCountLock); + + // If we have any waiting threads, send them a signal + if(haveWaiters) + SetEvent(mEvents[_CONDITION_EVENT_ONE]); +} +#endif + +#if defined(_TTHREAD_WIN32_) +void condition_variable::notify_all() +{ + // Are there any waiters? + EnterCriticalSection(&mWaitersCountLock); + bool haveWaiters = (mWaitersCount > 0); + LeaveCriticalSection(&mWaitersCountLock); + + // If we have any waiting threads, send them a signal + if(haveWaiters) + SetEvent(mEvents[_CONDITION_EVENT_ALL]); +} +#endif + + +//------------------------------------------------------------------------------ +// POSIX pthread_t to unique thread::id mapping logic. +// Note: Here we use a global thread safe std::map to convert instances of +// pthread_t to small thread identifier numbers (unique within one process). +// This method should be portable across different POSIX implementations. +//------------------------------------------------------------------------------ + +#if defined(_TTHREAD_POSIX_) +static thread::id _pthread_t_to_ID(const pthread_t &aHandle) +{ + static mutex idMapLock; + static std::map<pthread_t, unsigned long int> idMap; + static unsigned long int idCount(1); + + lock_guard<mutex> guard(idMapLock); + if(idMap.find(aHandle) == idMap.end()) + idMap[aHandle] = idCount ++; + return thread::id(idMap[aHandle]); +} +#endif // _TTHREAD_POSIX_ + + +//------------------------------------------------------------------------------ +// thread +//------------------------------------------------------------------------------ + +/// Information to pass to the new thread (what to run). +struct _thread_start_info { + void (*mFunction)(void *); ///< Pointer to the function to be executed. + void * mArg; ///< Function argument for the thread function. + thread * mThread; ///< Pointer to the thread object. +}; + +// Thread wrapper function. +#if defined(_TTHREAD_WIN32_) +unsigned WINAPI thread::wrapper_function(void * aArg) +#elif defined(_TTHREAD_POSIX_) +void * thread::wrapper_function(void * aArg) +#endif +{ + // Get thread startup information + _thread_start_info * ti = (_thread_start_info *) aArg; + + try + { + // Call the actual client thread function + ti->mFunction(ti->mArg); + } + catch(...) + { + // Uncaught exceptions will terminate the application (default behavior + // according to C++11) + std::terminate(); + } + + // The thread is no longer executing + lock_guard<mutex> guard(ti->mThread->mDataMutex); + ti->mThread->mNotAThread = true; + + // The thread is responsible for freeing the startup information + delete ti; + + return 0; +} + +thread::thread(void (*aFunction)(void *), void * aArg) +{ + // Serialize access to this thread structure + lock_guard<mutex> guard(mDataMutex); + + // Fill out the thread startup information (passed to the thread wrapper, + // which will eventually free it) + _thread_start_info * ti = new _thread_start_info; + ti->mFunction = aFunction; + ti->mArg = aArg; + ti->mThread = this; + + // The thread is now alive + mNotAThread = false; + + // Create the thread +#if defined(_TTHREAD_WIN32_) + mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID); +#elif defined(_TTHREAD_POSIX_) + if(pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0) + mHandle = 0; +#endif + + // Did we fail to create the thread? + if(!mHandle) + { + mNotAThread = true; + delete ti; + } +} + +thread::~thread() +{ + if(joinable()) + std::terminate(); +} + +void thread::join() +{ + if(joinable()) + { +#if defined(_TTHREAD_WIN32_) + WaitForSingleObject(mHandle, INFINITE); + CloseHandle(mHandle); +#elif defined(_TTHREAD_POSIX_) + pthread_join(mHandle, NULL); +#endif + } +} + +bool thread::joinable() const +{ + mDataMutex.lock(); + bool result = !mNotAThread; + mDataMutex.unlock(); + return result; +} + +void thread::detach() +{ + mDataMutex.lock(); + if(!mNotAThread) + { +#if defined(_TTHREAD_WIN32_) + CloseHandle(mHandle); +#elif defined(_TTHREAD_POSIX_) + pthread_detach(mHandle); +#endif + mNotAThread = true; + } + mDataMutex.unlock(); +} + +thread::id thread::get_id() const +{ + if(!joinable()) + return id(); +#if defined(_TTHREAD_WIN32_) + return id((unsigned long int) mWin32ThreadID); +#elif defined(_TTHREAD_POSIX_) + return _pthread_t_to_ID(mHandle); +#endif +} + +unsigned thread::hardware_concurrency() +{ +#if defined(_TTHREAD_WIN32_) + SYSTEM_INFO si; + GetSystemInfo(&si); + return (int) si.dwNumberOfProcessors; +#elif defined(_SC_NPROCESSORS_ONLN) + return (int) sysconf(_SC_NPROCESSORS_ONLN); +#elif defined(_SC_NPROC_ONLN) + return (int) sysconf(_SC_NPROC_ONLN); +#else + // The standard requires this function to return zero if the number of + // hardware cores could not be determined. + return 0; +#endif +} + + +//------------------------------------------------------------------------------ +// this_thread +//------------------------------------------------------------------------------ + +thread::id this_thread::get_id() +{ +#if defined(_TTHREAD_WIN32_) + return thread::id((unsigned long int) GetCurrentThreadId()); +#elif defined(_TTHREAD_POSIX_) + return _pthread_t_to_ID(pthread_self()); +#endif +} + +} diff --git a/src/uscxml/concurrency/tinythread.h b/src/uscxml/concurrency/tinythread.h new file mode 100644 index 0000000..aed7b58 --- /dev/null +++ b/src/uscxml/concurrency/tinythread.h @@ -0,0 +1,714 @@ +/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- +Copyright (c) 2010-2012 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef _TINYTHREAD_H_ +#define _TINYTHREAD_H_ + +/// @file +/// @mainpage TinyThread++ API Reference +/// +/// @section intro_sec Introduction +/// TinyThread++ is a minimal, portable implementation of basic threading +/// classes for C++. +/// +/// They closely mimic the functionality and naming of the C++11 standard, and +/// should be easily replaceable with the corresponding std:: variants. +/// +/// @section port_sec Portability +/// The Win32 variant uses the native Win32 API for implementing the thread +/// classes, while for other systems, the POSIX threads API (pthread) is used. +/// +/// @section class_sec Classes +/// In order to mimic the threading API of the C++11 standard, subsets of +/// several classes are provided. The fundamental classes are: +/// @li tthread::thread +/// @li tthread::mutex +/// @li tthread::recursive_mutex +/// @li tthread::condition_variable +/// @li tthread::lock_guard +/// @li tthread::fast_mutex +/// +/// @section misc_sec Miscellaneous +/// The following special keywords are available: #thread_local. +/// +/// For more detailed information (including additional classes), browse the +/// different sections of this documentation. A good place to start is: +/// tinythread.h. + +// Which platform are we on? +#if !defined(_TTHREAD_PLATFORM_DEFINED_) + #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) + #define _TTHREAD_WIN32_ + #else + #define _TTHREAD_POSIX_ + #endif + #define _TTHREAD_PLATFORM_DEFINED_ +#endif + +// Platform specific includes +#if defined(_TTHREAD_WIN32_) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #define __UNDEF_LEAN_AND_MEAN + #endif + #include <windows.h> + #ifdef __UNDEF_LEAN_AND_MEAN + #undef WIN32_LEAN_AND_MEAN + #undef __UNDEF_LEAN_AND_MEAN + #endif +#else + #include <pthread.h> + #include <signal.h> + #include <sched.h> + #include <unistd.h> +#endif + +// Generic includes +#include <ostream> + +/// TinyThread++ version (major number). +#define TINYTHREAD_VERSION_MAJOR 1 +/// TinyThread++ version (minor number). +#define TINYTHREAD_VERSION_MINOR 1 +/// TinyThread++ version (full version). +#define TINYTHREAD_VERSION (TINYTHREAD_VERSION_MAJOR * 100 + TINYTHREAD_VERSION_MINOR) + +// Do we have a fully featured C++11 compiler? +#if (__cplusplus > 199711L) || (defined(__STDCXX_VERSION__) && (__STDCXX_VERSION__ >= 201001L)) + #define _TTHREAD_CPP11_ +#endif + +// ...at least partial C++11? +#if defined(_TTHREAD_CPP11_) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__) + #define _TTHREAD_CPP11_PARTIAL_ +#endif + +// Macro for disabling assignments of objects. +#ifdef _TTHREAD_CPP11_PARTIAL_ + #define _TTHREAD_DISABLE_ASSIGNMENT(name) \ + name(const name&) = delete; \ + name& operator=(const name&) = delete; +#else + #define _TTHREAD_DISABLE_ASSIGNMENT(name) \ + name(const name&); \ + name& operator=(const name&); +#endif + +/// @def thread_local +/// Thread local storage keyword. +/// A variable that is declared with the @c thread_local keyword makes the +/// value of the variable local to each thread (known as thread-local storage, +/// or TLS). Example usage: +/// @code +/// // This variable is local to each thread. +/// thread_local int variable; +/// @endcode +/// @note The @c thread_local keyword is a macro that maps to the corresponding +/// compiler directive (e.g. @c __declspec(thread)). While the C++11 standard +/// allows for non-trivial types (e.g. classes with constructors and +/// destructors) to be declared with the @c thread_local keyword, most pre-C++11 +/// compilers only allow for trivial types (e.g. @c int). So, to guarantee +/// portable code, only use trivial types for thread local storage. +/// @note This directive is currently not supported on Mac OS X (it will give +/// a compiler error), since compile-time TLS is not supported in the Mac OS X +/// executable format. Also, some older versions of MinGW (before GCC 4.x) do +/// not support this directive. +/// @hideinitializer + +#if !defined(_TTHREAD_CPP11_) && !defined(thread_local) + #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) + #define thread_local __thread + #else + #define thread_local __declspec(thread) + #endif +#endif + + +/// Main name space for TinyThread++. +/// This namespace is more or less equivalent to the @c std namespace for the +/// C++11 thread classes. For instance, the tthread::mutex class corresponds to +/// the std::mutex class. +namespace tthread { + +/// Mutex class. +/// This is a mutual exclusion object for synchronizing access to shared +/// memory areas for several threads. The mutex is non-recursive (i.e. a +/// program may deadlock if the thread that owns a mutex object calls lock() +/// on that object). +/// @see recursive_mutex +class mutex { + public: + /// Constructor. + mutex() +#if defined(_TTHREAD_WIN32_) + : mAlreadyLocked(false) +#endif + { +#if defined(_TTHREAD_WIN32_) + InitializeCriticalSection(&mHandle); +#else + pthread_mutex_init(&mHandle, NULL); +#endif + } + + /// Destructor. + ~mutex() + { +#if defined(_TTHREAD_WIN32_) + DeleteCriticalSection(&mHandle); +#else + pthread_mutex_destroy(&mHandle); +#endif + } + + /// Lock the mutex. + /// The method will block the calling thread until a lock on the mutex can + /// be obtained. The mutex remains locked until @c unlock() is called. + /// @see lock_guard + inline void lock() + { +#if defined(_TTHREAD_WIN32_) + EnterCriticalSection(&mHandle); + while(mAlreadyLocked) Sleep(1000); // Simulate deadlock... + mAlreadyLocked = true; +#else + pthread_mutex_lock(&mHandle); +#endif + } + + /// Try to lock the mutex. + /// The method will try to lock the mutex. If it fails, the function will + /// return immediately (non-blocking). + /// @return @c true if the lock was acquired, or @c false if the lock could + /// not be acquired. + inline bool try_lock() + { +#if defined(_TTHREAD_WIN32_) + bool ret = (TryEnterCriticalSection(&mHandle) ? true : false); + if(ret && mAlreadyLocked) + { + LeaveCriticalSection(&mHandle); + ret = false; + } + return ret; +#else + return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; +#endif + } + + /// Unlock the mutex. + /// If any threads are waiting for the lock on this mutex, one of them will + /// be unblocked. + inline void unlock() + { +#if defined(_TTHREAD_WIN32_) + mAlreadyLocked = false; + LeaveCriticalSection(&mHandle); +#else + pthread_mutex_unlock(&mHandle); +#endif + } + + _TTHREAD_DISABLE_ASSIGNMENT(mutex) + + private: +#if defined(_TTHREAD_WIN32_) + CRITICAL_SECTION mHandle; + bool mAlreadyLocked; +#else + pthread_mutex_t mHandle; +#endif + + friend class condition_variable; +}; + +/// Recursive mutex class. +/// This is a mutual exclusion object for synchronizing access to shared +/// memory areas for several threads. The mutex is recursive (i.e. a thread +/// may lock the mutex several times, as long as it unlocks the mutex the same +/// number of times). +/// @see mutex +class recursive_mutex { + public: + /// Constructor. + recursive_mutex() + { +#if defined(_TTHREAD_WIN32_) + InitializeCriticalSection(&mHandle); +#else + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mHandle, &attr); +#endif + } + + /// Destructor. + ~recursive_mutex() + { +#if defined(_TTHREAD_WIN32_) + DeleteCriticalSection(&mHandle); +#else + pthread_mutex_destroy(&mHandle); +#endif + } + + /// Lock the mutex. + /// The method will block the calling thread until a lock on the mutex can + /// be obtained. The mutex remains locked until @c unlock() is called. + /// @see lock_guard + inline void lock() + { +#if defined(_TTHREAD_WIN32_) + EnterCriticalSection(&mHandle); +#else + pthread_mutex_lock(&mHandle); +#endif + } + + /// Try to lock the mutex. + /// The method will try to lock the mutex. If it fails, the function will + /// return immediately (non-blocking). + /// @return @c true if the lock was acquired, or @c false if the lock could + /// not be acquired. + inline bool try_lock() + { +#if defined(_TTHREAD_WIN32_) + return TryEnterCriticalSection(&mHandle) ? true : false; +#else + return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; +#endif + } + + /// Unlock the mutex. + /// If any threads are waiting for the lock on this mutex, one of them will + /// be unblocked. + inline void unlock() + { +#if defined(_TTHREAD_WIN32_) + LeaveCriticalSection(&mHandle); +#else + pthread_mutex_unlock(&mHandle); +#endif + } + + _TTHREAD_DISABLE_ASSIGNMENT(recursive_mutex) + + private: +#if defined(_TTHREAD_WIN32_) + CRITICAL_SECTION mHandle; +#else + pthread_mutex_t mHandle; +#endif + + friend class condition_variable; +}; + +/// Lock guard class. +/// The constructor locks the mutex, and the destructor unlocks the mutex, so +/// the mutex will automatically be unlocked when the lock guard goes out of +/// scope. Example usage: +/// @code +/// mutex m; +/// int counter; +/// +/// void increment() +/// { +/// lock_guard<mutex> guard(m); +/// ++ counter; +/// } +/// @endcode + +template <class T> +class lock_guard { + public: + typedef T mutex_type; + + lock_guard() : mMutex(0) {} + + /// The constructor locks the mutex. + explicit lock_guard(mutex_type &aMutex) + { + mMutex = &aMutex; + mMutex->lock(); + } + + /// The destructor unlocks the mutex. + ~lock_guard() + { + if(mMutex) + mMutex->unlock(); + } + + private: + mutex_type * mMutex; +}; + +/// Condition variable class. +/// This is a signalling object for synchronizing the execution flow for +/// several threads. Example usage: +/// @code +/// // Shared data and associated mutex and condition variable objects +/// int count; +/// mutex m; +/// condition_variable cond; +/// +/// // Wait for the counter to reach a certain number +/// void wait_counter(int targetCount) +/// { +/// lock_guard<mutex> guard(m); +/// while(count < targetCount) +/// cond.wait(m); +/// } +/// +/// // Increment the counter, and notify waiting threads +/// void increment() +/// { +/// lock_guard<mutex> guard(m); +/// ++ count; +/// cond.notify_all(); +/// } +/// @endcode +class condition_variable { + public: + /// Constructor. +#if defined(_TTHREAD_WIN32_) + condition_variable(); +#else + condition_variable() + { + pthread_cond_init(&mHandle, NULL); + } +#endif + + /// Destructor. +#if defined(_TTHREAD_WIN32_) + ~condition_variable(); +#else + ~condition_variable() + { + pthread_cond_destroy(&mHandle); + } +#endif + + /// Wait for the condition. + /// The function will block the calling thread until the condition variable + /// is woken by @c notify_one(), @c notify_all() or a spurious wake up. + /// @param[in] aMutex A mutex that will be unlocked when the wait operation + /// starts, an locked again as soon as the wait operation is finished. + template <class _mutexT> + inline void wait(_mutexT &aMutex) + { +#if defined(_TTHREAD_WIN32_) + // Increment number of waiters + EnterCriticalSection(&mWaitersCountLock); + ++ mWaitersCount; + LeaveCriticalSection(&mWaitersCountLock); + + // Release the mutex while waiting for the condition (will decrease + // the number of waiters when done)... + aMutex.unlock(); + _wait(); + aMutex.lock(); +#else + pthread_cond_wait(&mHandle, &aMutex.mHandle); +#endif + } + + /// Notify one thread that is waiting for the condition. + /// If at least one thread is blocked waiting for this condition variable, + /// one will be woken up. + /// @note Only threads that started waiting prior to this call will be + /// woken up. +#if defined(_TTHREAD_WIN32_) + void notify_one(); +#else + inline void notify_one() + { + pthread_cond_signal(&mHandle); + } +#endif + + /// Notify all threads that are waiting for the condition. + /// All threads that are blocked waiting for this condition variable will + /// be woken up. + /// @note Only threads that started waiting prior to this call will be + /// woken up. +#if defined(_TTHREAD_WIN32_) + void notify_all(); +#else + inline void notify_all() + { + pthread_cond_broadcast(&mHandle); + } +#endif + + _TTHREAD_DISABLE_ASSIGNMENT(condition_variable) + + private: +#if defined(_TTHREAD_WIN32_) + void _wait(); + HANDLE mEvents[2]; ///< Signal and broadcast event HANDLEs. + unsigned int mWaitersCount; ///< Count of the number of waiters. + CRITICAL_SECTION mWaitersCountLock; ///< Serialize access to mWaitersCount. +#else + pthread_cond_t mHandle; +#endif +}; + + +/// Thread class. +class thread { + public: +#if defined(_TTHREAD_WIN32_) + typedef HANDLE native_handle_type; +#else + typedef pthread_t native_handle_type; +#endif + + class id; + + /// Default constructor. + /// Construct a @c thread object without an associated thread of execution + /// (i.e. non-joinable). + thread() : mHandle(0), mNotAThread(true) +#if defined(_TTHREAD_WIN32_) + , mWin32ThreadID(0) +#endif + {} + + /// Thread starting constructor. + /// Construct a @c thread object with a new thread of execution. + /// @param[in] aFunction A function pointer to a function of type: + /// <tt>void fun(void * arg)</tt> + /// @param[in] aArg Argument to the thread function. + /// @note This constructor is not fully compatible with the standard C++ + /// thread class. It is more similar to the pthread_create() (POSIX) and + /// CreateThread() (Windows) functions. + thread(void (*aFunction)(void *), void * aArg); + + /// Destructor. + /// @note If the thread is joinable upon destruction, @c std::terminate() + /// will be called, which terminates the process. It is always wise to do + /// @c join() before deleting a thread object. + ~thread(); + + /// Wait for the thread to finish (join execution flows). + /// After calling @c join(), the thread object is no longer associated with + /// a thread of execution (i.e. it is not joinable, and you may not join + /// with it nor detach from it). + void join(); + + /// Check if the thread is joinable. + /// A thread object is joinable if it has an associated thread of execution. + bool joinable() const; + + /// Detach from the thread. + /// After calling @c detach(), the thread object is no longer assicated with + /// a thread of execution (i.e. it is not joinable). The thread continues + /// execution without the calling thread blocking, and when the thread + /// ends execution, any owned resources are released. + void detach(); + + /// Return the thread ID of a thread object. + id get_id() const; + + /// Get the native handle for this thread. + /// @note Under Windows, this is a @c HANDLE, and under POSIX systems, this + /// is a @c pthread_t. + inline native_handle_type native_handle() + { + return mHandle; + } + + /// Determine the number of threads which can possibly execute concurrently. + /// This function is useful for determining the optimal number of threads to + /// use for a task. + /// @return The number of hardware thread contexts in the system. + /// @note If this value is not defined, the function returns zero (0). + static unsigned hardware_concurrency(); + + _TTHREAD_DISABLE_ASSIGNMENT(thread) + + private: + native_handle_type mHandle; ///< Thread handle. + mutable mutex mDataMutex; ///< Serializer for access to the thread private data. + bool mNotAThread; ///< True if this object is not a thread of execution. +#if defined(_TTHREAD_WIN32_) + unsigned int mWin32ThreadID; ///< Unique thread ID (filled out by _beginthreadex). +#endif + + // This is the internal thread wrapper function. +#if defined(_TTHREAD_WIN32_) + static unsigned WINAPI wrapper_function(void * aArg); +#else + static void * wrapper_function(void * aArg); +#endif +}; + +/// Thread ID. +/// The thread ID is a unique identifier for each thread. +/// @see thread::get_id() +class thread::id { + public: + /// Default constructor. + /// The default constructed ID is that of thread without a thread of + /// execution. + id() : mId(0) {}; + + id(unsigned long int aId) : mId(aId) {}; + + id(const id& aId) : mId(aId.mId) {}; + + inline id & operator=(const id &aId) + { + mId = aId.mId; + return *this; + } + + inline friend bool operator==(const id &aId1, const id &aId2) + { + return (aId1.mId == aId2.mId); + } + + inline friend bool operator!=(const id &aId1, const id &aId2) + { + return (aId1.mId != aId2.mId); + } + + inline friend bool operator<=(const id &aId1, const id &aId2) + { + return (aId1.mId <= aId2.mId); + } + + inline friend bool operator<(const id &aId1, const id &aId2) + { + return (aId1.mId < aId2.mId); + } + + inline friend bool operator>=(const id &aId1, const id &aId2) + { + return (aId1.mId >= aId2.mId); + } + + inline friend bool operator>(const id &aId1, const id &aId2) + { + return (aId1.mId > aId2.mId); + } + + inline friend std::ostream& operator <<(std::ostream &os, const id &obj) + { + os << obj.mId; + return os; + } + + private: + unsigned long int mId; +}; + + +// Related to <ratio> - minimal to be able to support chrono. +typedef long long __intmax_t; + +/// Minimal implementation of the @c ratio class. This class provides enough +/// functionality to implement some basic @c chrono classes. +template <__intmax_t N, __intmax_t D = 1> class ratio { + public: + static double _as_double() { return double(N) / double(D); } +}; + +/// Minimal implementation of the @c chrono namespace. +/// The @c chrono namespace provides types for specifying time intervals. +namespace chrono { + /// Duration template class. This class provides enough functionality to + /// implement @c this_thread::sleep_for(). + template <class _Rep, class _Period = ratio<1> > class duration { + private: + _Rep rep_; + public: + typedef _Rep rep; + typedef _Period period; + + /// Construct a duration object with the given duration. + template <class _Rep2> + explicit duration(const _Rep2& r) : rep_(r) {}; + + /// Return the value of the duration object. + rep count() const + { + return rep_; + } + }; + + // Standard duration types. + typedef duration<__intmax_t, ratio<1, 1000000000> > nanoseconds; ///< Duration with the unit nanoseconds. + typedef duration<__intmax_t, ratio<1, 1000000> > microseconds; ///< Duration with the unit microseconds. + typedef duration<__intmax_t, ratio<1, 1000> > milliseconds; ///< Duration with the unit milliseconds. + typedef duration<__intmax_t> seconds; ///< Duration with the unit seconds. + typedef duration<__intmax_t, ratio<60> > minutes; ///< Duration with the unit minutes. + typedef duration<__intmax_t, ratio<3600> > hours; ///< Duration with the unit hours. +} + +/// The namespace @c this_thread provides methods for dealing with the +/// calling thread. +namespace this_thread { + /// Return the thread ID of the calling thread. + thread::id get_id(); + + /// Yield execution to another thread. + /// Offers the operating system the opportunity to schedule another thread + /// that is ready to run on the current processor. + inline void yield() + { +#if defined(_TTHREAD_WIN32_) + Sleep(0); +#else + sched_yield(); +#endif + } + + /// Blocks the calling thread for a period of time. + /// @param[in] aTime Minimum time to put the thread to sleep. + /// Example usage: + /// @code + /// // Sleep for 100 milliseconds + /// this_thread::sleep_for(chrono::milliseconds(100)); + /// @endcode + /// @note Supported duration types are: nanoseconds, microseconds, + /// milliseconds, seconds, minutes and hours. + template <class _Rep, class _Period> void sleep_for(const chrono::duration<_Rep, _Period>& aTime) + { +#if defined(_TTHREAD_WIN32_) + Sleep(int(double(aTime.count()) * (1000.0 * _Period::_as_double()) + 0.5)); +#else + usleep(int(double(aTime.count()) * (1000000.0 * _Period::_as_double()) + 0.5)); +#endif + } +} + +} + +// Define/macro cleanup +#undef _TTHREAD_DISABLE_ASSIGNMENT + +#endif // _TINYTHREAD_H_ diff --git a/src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp new file mode 100644 index 0000000..e31438a --- /dev/null +++ b/src/uscxml/datamodel/ecmascript/v8/V8DataModel.cpp @@ -0,0 +1,289 @@ +#include "uscxml/datamodel/ecmascript/v8/V8DataModel.h" +#include "uscxml/Message.h" + +namespace uscxml { + +V8DataModel::V8DataModel() { +// _contexts.push_back(v8::Context::New()); +} + +DataModel* V8DataModel::create(Interpreter* interpreter) { + V8DataModel* dm = new V8DataModel(); + dm->_interpreter = interpreter; + v8::Locker locker; + v8::HandleScope scope; + + // see http://stackoverflow.com/questions/3171418/v8-functiontemplate-class-instance + dm->_globalTemplate = v8::Persistent<v8::ObjectTemplate>(v8::ObjectTemplate::New()); + dm->_globalTemplate->Set(v8::String::New("In"), v8::FunctionTemplate::New(jsIn, v8::External::New(reinterpret_cast<void*>(this)))); + + dm->_contexts.push_back(v8::Context::New(0, _globalTemplate)); + dm->setName(interpreter->getName()); + dm->setSessionId(interpreter->getSessionId()); + dm->eval("_ioprocessors = {};"); + return dm; +} + +void V8DataModel::setSessionId(const std::string& sessionId) { + _sessionId = sessionId; + v8::Locker locker; + v8::HandleScope handleScope; + v8::Context::Scope contextScope(_contexts.front()); + v8::Handle<v8::Object> global = _contexts.front()->Global(); + + global->Set(v8::String::New("_sessionid"), v8::String::New(sessionId.c_str())); +} + +void V8DataModel::setName(const std::string& name) { + _name = name; + v8::HandleScope handleScope; + v8::Context::Scope contextScope(_contexts.front()); + v8::Handle<v8::Object> global = _contexts.front()->Global(); + + global->Set(v8::String::New("_name"), v8::String::New(name.c_str())); +} + +V8DataModel::~V8DataModel() { + while(_contexts.size() > 0) { + _contexts.back().Dispose(); + _contexts.pop_back(); + } +} + +void V8DataModel::pushContext() { + _contexts.push_back(_contexts.back().New(_contexts.back())); +} + +void V8DataModel::popContext() { + if (_contexts.size() > 1) { + _contexts.back().Dispose(); + _contexts.pop_back(); + } +} + +void V8DataModel::initialize() { +} + +void V8DataModel::setEvent(Event& event) { + _event = event; + v8::Locker locker; + v8::HandleScope handleScope; + v8::Context::Scope contextScope(_contexts.front()); + v8::Handle<v8::Object> global = _contexts.front()->Global(); + + // this is unfortunate - can't we store the template in the object? + if (_eventTemplate.IsEmpty()) { + v8::Handle<v8::ObjectTemplate> localEventTemplate = v8::ObjectTemplate::New(); + localEventTemplate->SetInternalFieldCount(1); // we only have a single C++ object + localEventTemplate->SetAccessor(v8::String::New("name"), V8DataModel::jsGetEventName); + localEventTemplate->SetAccessor(v8::String::New("type"), V8DataModel::jsGetEventType); + localEventTemplate->SetAccessor(v8::String::New("sendid"), V8DataModel::jsGetEventSendId); + localEventTemplate->SetAccessor(v8::String::New("origin"), V8DataModel::jsGetEventOrigin); + localEventTemplate->SetAccessor(v8::String::New("origintype"), V8DataModel::jsGetEventOriginType); + localEventTemplate->SetAccessor(v8::String::New("invokeid"), V8DataModel::jsGetEventInvokeId); + _eventTemplate = v8::Persistent<v8::ObjectTemplate>::New(localEventTemplate); + } + + assert(_eventTemplate->InternalFieldCount() == 1); + v8::Handle<v8::Object> eventJS = _eventTemplate->NewInstance(); + eventJS->SetInternalField(0, v8::External::New(&event)); + + eventJS->Set(v8::String::New("data"), getDataAsValue(event)); // set data part of _event + global->Set(v8::String::New("_event"), eventJS); +} + +void V8DataModel::setData(const std::string& key, Data& data) { + v8::Locker locker; + v8::HandleScope handleScope; + v8::Context::Scope contextScope(_contexts.front()); + v8::Handle<v8::Object> global = _contexts.front()->Global(); + global->Set(v8::String::New(key.c_str()), getDataAsValue(data)); +} + +v8::Handle<v8::Value> V8DataModel::getDataAsValue(Data& data) { + if (data.compound.size() > 0) { + v8::Handle<v8::Object> value = v8::Array::New(); + std::map<std::string, Data>::iterator compoundIter = data.compound.begin(); + while(compoundIter != data.compound.end()) { + value->Set(v8::String::New(compoundIter->first.c_str()), getDataAsValue(compoundIter->second)); + compoundIter++; + } + return value; + } + if (data.array.size() > 0) { + v8::Handle<v8::Object> value = v8::Array::New(); + std::vector<Data>::iterator arrayIter = data.array.begin(); + uint32_t index = 0; + while(arrayIter != data.array.end()) { + value->Set(index++, getDataAsValue(*arrayIter)); + arrayIter++; + } + return value; + } + if (data.type == Data::VERBATIM) { + return v8::String::New(data.atom.c_str()); + } else { + return evalAsValue(data.atom); + } +} + +v8::Handle<v8::Value> V8DataModel::jsIn(const v8::Arguments& args) { + V8DataModel* THIS = static_cast<V8DataModel*>(v8::External::Unwrap(args.Data())); + for (unsigned int i = 0; i < args.Length(); i++) { + if (args[i]->IsString()) { + std::string stateName(*v8::String::AsciiValue(args[i]->ToString())); + if (Interpreter::isMember(THIS->_interpreter->getState(stateName), THIS->_interpreter->getConfiguration())) { + continue; + } + } + return v8::Boolean::New(false); + } + return v8::Boolean::New(true); +} + +v8::Handle<v8::Value> V8DataModel::jsGetEventName(v8::Local<v8::String> property, + const v8::AccessorInfo &info) { + Event* event = static_cast<Event*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()); + return v8::String::New(event->name.c_str()); +} + +v8::Handle<v8::Value> V8DataModel::jsGetEventType(v8::Local<v8::String> property, + const v8::AccessorInfo &info) { + Event* event = static_cast<Event*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()); + switch (event->type) { + case Event::PLATFORM: + return v8::String::New("platform"); + break; + case Event::INTERNAL: + return v8::String::New("internal"); + break; + case Event::EXTERNAL: + return v8::String::New("external"); + break; + default: + return v8::String::New(""); + break; + } +} + +v8::Handle<v8::Value> V8DataModel::jsGetEventSendId(v8::Local<v8::String> property, + const v8::AccessorInfo &info) { + Event* event = static_cast<Event*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()); + return v8::String::New(event->sendid.c_str()); + +} + +v8::Handle<v8::Value> V8DataModel::jsGetEventOrigin(v8::Local<v8::String> property, + const v8::AccessorInfo &info) { + Event* event = static_cast<Event*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()); + return v8::String::New(event->origin.c_str()); +} + +v8::Handle<v8::Value> V8DataModel::jsGetEventOriginType(v8::Local<v8::String> property, + const v8::AccessorInfo &info) { + Event* event = static_cast<Event*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()); + return v8::String::New(event->origintype.c_str()); +} + +v8::Handle<v8::Value> V8DataModel::jsGetEventInvokeId(v8::Local<v8::String> property, + const v8::AccessorInfo &info) { + Event* event = static_cast<Event*>(v8::Local<v8::External>::Cast(info.Holder()->GetInternalField(0))->Value()); + return v8::String::New(event->invokeid.c_str()); +} + +bool V8DataModel::validate(const std::string& location, const std::string& schema) { + return true; +} + +uint32_t V8DataModel::getLength(const std::string& expr) { + v8::Locker locker; + v8::HandleScope handleScope; + v8::Context::Scope contextScope(_contexts.back()); + v8::Handle<v8::Array> result = evalAsValue(expr).As<v8::Array>(); + return result->Length(); +} + +void V8DataModel::eval(const std::string& expr) { + v8::Locker locker; + v8::HandleScope handleScope; + v8::Context::Scope contextScope(_contexts.back()); + evalAsValue(expr); +} + +bool V8DataModel::evalAsBool(const std::string& expr) { + v8::Locker locker; + v8::HandleScope handleScope; + v8::Context::Scope contextScope(_contexts.back()); + v8::Handle<v8::Value> result = evalAsValue(expr); + return(result->ToBoolean()->BooleanValue()); +} + +std::string V8DataModel::evalAsString(const std::string& expr) { + v8::Locker locker; + v8::HandleScope handleScope; + v8::Context::Scope contextScope(_contexts.back()); + v8::Handle<v8::Value> result = evalAsValue(expr); + v8::String::AsciiValue data(result->ToString()); + return std::string(*data); +} + +void V8DataModel::assign(const std::string& location, const std::string& expr) { + v8::Locker locker; + v8::HandleScope handleScope; + v8::Context::Scope contextScope(_contexts.back()); + evalAsValue((location + " = " + expr).c_str()); +} + +v8::Handle<v8::Value> V8DataModel::evalAsValue(const std::string& expr) { + v8::TryCatch tryCatch; + v8::Handle<v8::String> source = v8::String::New(expr.c_str()); + v8::Handle<v8::Script> script = v8::Script::Compile(source); + + v8::Handle<v8::Value> result; + if (!script.IsEmpty()) + result = script->Run(); + + if (script.IsEmpty() || result.IsEmpty()) { + // throw an exception + assert(tryCatch.HasCaught()); + Event exceptionEvent; + exceptionEvent.name = "error.execution"; + + std::string exceptionString(*v8::String::AsciiValue(tryCatch.Exception())); + exceptionEvent.compound["exception"] = Data(exceptionString, Data::VERBATIM);; + + v8::Handle<v8::Message> message = tryCatch.Message(); + if (!message.IsEmpty()) { + std::string filename(*v8::String::AsciiValue(message->GetScriptResourceName())); + exceptionEvent.compound["filename"] = Data(filename, Data::VERBATIM); + + std::string sourceLine(*v8::String::AsciiValue(message->GetSourceLine())); + exceptionEvent.compound["sourceline"] = Data(sourceLine, Data::VERBATIM); + + std::stringstream ssLineNumber; + int lineNumber = message->GetLineNumber(); + ssLineNumber << lineNumber; + exceptionEvent.compound["linenumber"] = Data(ssLineNumber.str()); + + int startColumn = message->GetStartColumn(); + int endColumn = message->GetEndColumn(); + std::stringstream ssUnderline; + for (int i = 0; i < startColumn; i++) + ssUnderline << " "; + for (int i = startColumn; i < endColumn; i++) + ssUnderline << "^"; + exceptionEvent.compound["sourcemark"] = Data(ssUnderline.str(), Data::VERBATIM); + + std::string stackTrace(*v8::String::AsciiValue(tryCatch.StackTrace())); + exceptionEvent.compound["stacktrace"] = Data(stackTrace, Data::VERBATIM); + + } + + _interpreter->receiveInternal(exceptionEvent); + throw(exceptionEvent); + } + + return result; +} + +}
\ No newline at end of file diff --git a/src/uscxml/datamodel/ecmascript/v8/V8DataModel.h b/src/uscxml/datamodel/ecmascript/v8/V8DataModel.h new file mode 100644 index 0000000..7522679 --- /dev/null +++ b/src/uscxml/datamodel/ecmascript/v8/V8DataModel.h @@ -0,0 +1,73 @@ +#ifndef V8DATAMODEL_H_KN8TWG0V +#define V8DATAMODEL_H_KN8TWG0V + +#include "uscxml/Interpreter.h" +#include <list> +#include <v8.h> + +namespace uscxml { + class Event; + class Data; +} + +namespace uscxml { + +class V8DataModel : public DataModel { +public: + V8DataModel(); + virtual ~V8DataModel(); + virtual DataModel* create(Interpreter* interpreter); + + virtual void initialize(); + virtual void setSessionId(const std::string& sessionId); + virtual void setName(const std::string& name); + virtual void setEvent(Event& event); + virtual void setData(const std::string& key, Data& event); + virtual v8::Handle<v8::Value> getDataAsValue(Data& data); + + 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 std::string& expr); + + virtual std::string evalAsString(const std::string& expr); + virtual bool evalAsBool(const std::string& expr); + + static v8::Handle<v8::Value> jsGetEventName(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> jsGetEventType(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> jsGetEventSendId(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> jsGetEventOrigin(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> jsGetEventOriginType(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> jsGetEventInvokeId(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + + static v8::Handle<v8::Value> jsIn(const v8::Arguments& args); + + +protected: + std::list<v8::Persistent<v8::Context> > _contexts; + Interpreter* _interpreter; + + std::string _sessionId; + std::string _name; + + Event _event; + v8::Persistent<v8::ObjectTemplate> _globalTemplate; + v8::Persistent<v8::ObjectTemplate> _eventTemplate; + + v8::Handle<v8::Value> evalAsValue(const std::string& expr); + +}; + +} + +#endif /* end of include guard: V8DATAMODEL_H_KN8TWG0V */ diff --git a/src/uscxml/ioprocessor/basichttp/README.md b/src/uscxml/ioprocessor/basichttp/README.md new file mode 100644 index 0000000..de89944 --- /dev/null +++ b/src/uscxml/ioprocessor/basichttp/README.md @@ -0,0 +1,2 @@ +Only the libevent basichttp ioprocessor is supported. Mongoose seemed somewhat +unmaintained and pion comes with too many dependencies.
\ No newline at end of file diff --git a/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp b/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp new file mode 100644 index 0000000..c06c7e8 --- /dev/null +++ b/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.cpp @@ -0,0 +1,361 @@ +#include "uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.h" +#include "uscxml/Message.h" +#include <iostream> +#include <event2/dns.h> +#include <event2/buffer.h> +#include <event2/keyvalq_struct.h> + +#include <string.h> + +#include <io/uri.hpp> +#include <glog/logging.h> + +#include <netdb.h> +#include <arpa/inet.h> + +namespace uscxml { +namespace io { +namespace libevent { + +// see http://www.w3.org/TR/scxml/#BasicHTTPEventProcessor + +EventIOProcessor::EventIOProcessor() { +} + +EventIOProcessor::~EventIOProcessor() { + _eventQueue.stop(); + EventIOServer* httpServer = EventIOServer::getInstance(); + httpServer->unregisterProcessor(this); +} + +IOProcessor* EventIOProcessor::create(Interpreter* interpreter) { + EventIOProcessor* io = new EventIOProcessor(); + io->_interpreter = interpreter; + + io->_dns = evdns_base_new(io->_eventQueue._eventLoop, 1); + assert(io->_dns); + assert(evdns_base_count_nameservers(io->_dns) > 0); + + // register at http server + EventIOServer* httpServer = EventIOServer::getInstance(); + httpServer->registerProcessor(io); + + io->start(); + return io; +} + +void EventIOProcessor::start() { + _eventQueue.start(); +} + +void EventIOProcessor::send(SendRequest& req) { + // I cant figure out how to copy the reference into the struct :( + _sendData[req.sendid].req = req; + _sendData[req.sendid].ioProcessor = this; + + if (req.delayMs > 0) { + LOG(INFO) << "Enqueing HTTP send request"; + _eventQueue.addEvent(req.sendid, EventIOProcessor::httpMakeSendReq, req.delayMs, &_sendData[req.sendid]); + } else { + LOG(INFO) << "Sending HTTP send request"; + EventIOProcessor::httpMakeSendReq(&_sendData[req.sendid], req.sendid); + } +} + +void EventIOProcessor::httpMakeSendReq(void* userdata, std::string eventName) { + SendData* sendData = ((SendData*)userdata); + EventIOProcessor* THIS = sendData->ioProcessor; + int err = 0; + char uriBuf[1024]; + + struct evhttp_uri* targetURI = evhttp_uri_parse(sendData->req.target.c_str()); + if (evhttp_uri_get_port(targetURI) == 0) + evhttp_uri_set_port(targetURI, 80); + const char* hostName = evhttp_uri_get_host(targetURI); + + // use synchronous dns resolving for multicast dns + if(strlen(hostName) >= strlen(".local")) { + if(strcmp(hostName + strlen(hostName) - strlen(".local"), ".local") == 0) { + evhttp_uri_set_host(targetURI, EventIOServer::syncResolve(hostName).c_str()); + } + } + evhttp_uri_join(targetURI, uriBuf, 1024); + + LOG(INFO) << "URI for send request: " << uriBuf << std::endl; + + std::stringstream ssEndPoint; + ssEndPoint << evhttp_uri_get_host(targetURI) << ":" << evhttp_uri_get_port(targetURI); + std::string endPoint = ssEndPoint.str(); + + std::stringstream ssLocalURI; + ssLocalURI << evhttp_uri_get_path(targetURI) << evhttp_uri_get_fragment(targetURI); + std::string localURI = ssLocalURI.str(); + + if (THIS->_httpConnections.find(endPoint) == THIS->_httpConnections.end()) + THIS->_httpConnections[endPoint] = evhttp_connection_base_new(THIS->_eventQueue._eventLoop, THIS->_dns, evhttp_uri_get_host(targetURI), evhttp_uri_get_port(targetURI)); + + struct evhttp_connection* httpConn = THIS->_httpConnections[endPoint]; + struct evhttp_request* httpReq = evhttp_request_new(EventIOProcessor::httpSendReqDone, userdata); + +#if 0 + // event name + if (sendData->req.event.size() > 0) { + evhttp_add_header(evhttp_request_get_output_headers(httpReq), "_scxmleventname", evhttp_encode_uri(sendData->req.event.c_str())); + } + + // event namelist + if (sendData->req.namelist.size() > 0) { + std::map<std::string, std::string>::iterator namelistIter = sendData->req.namelist.begin(); + while (namelistIter != sendData->req.namelist.end()) { + evhttp_add_header(evhttp_request_get_output_headers(httpReq), + namelistIter->first.c_str(), + evhttp_encode_uri(namelistIter->second.c_str())); + namelistIter++; + } + } + + // event params + if (sendData->req.params.size() > 0) { + std::map<std::string, std::string>::iterator paramIter = sendData->req.params.begin(); + while (paramIter != sendData->req.params.end()) { + evhttp_add_header(evhttp_request_get_output_headers(httpReq), + paramIter->first.c_str(), + evhttp_encode_uri(paramIter->second.c_str())); + paramIter++; + } + } + + // content + if (sendData->req.content.size() > 0) + evbuffer_add(evhttp_request_get_output_buffer(httpReq), sendData->req.content.c_str(), sendData->req.content.size()); +#endif + + evhttp_add_header(evhttp_request_get_output_headers(httpReq), "_scxmleventstruct", evhttp_encode_uri(sendData->req.toXMLString().c_str())); + + + THIS->_httpRequests[sendData->req.sendid] = httpReq; + err = evhttp_make_request(httpConn, + httpReq, + EVHTTP_REQ_POST, localURI.c_str()); + if (err) { + LOG(ERROR) << "Could not make http request to " << sendData->req.target; + } +} + +void EventIOProcessor::httpRecvReq(struct evhttp_request *req, void *arg) { + + const char *cmdtype; + struct evkeyvalq *headers; + struct evkeyval *header; + struct evbuffer *buf; + + switch (evhttp_request_get_command(req)) { + case EVHTTP_REQ_GET: cmdtype = "GET"; break; + case EVHTTP_REQ_POST: cmdtype = "POST"; break; + case EVHTTP_REQ_HEAD: cmdtype = "HEAD"; break; + case EVHTTP_REQ_PUT: cmdtype = "PUT"; break; + case EVHTTP_REQ_DELETE: cmdtype = "DELETE"; break; + case EVHTTP_REQ_OPTIONS: cmdtype = "OPTIONS"; break; + case EVHTTP_REQ_TRACE: cmdtype = "TRACE"; break; + case EVHTTP_REQ_CONNECT: cmdtype = "CONNECT"; break; + case EVHTTP_REQ_PATCH: cmdtype = "PATCH"; break; + default: cmdtype = "unknown"; break; + } + + Event reqEvent; + reqEvent.type = Event::EXTERNAL; + + // map headers to event structure + headers = evhttp_request_get_input_headers(req); + for (header = headers->tqh_first; header; + header = header->next.tqe_next) { +// std::cout << "Header: " << header->key << std::endl; +// std::cout << "Value: " << evhttp_decode_uri(header->value) << std::endl; + if (boost::iequals("_scxmleventstruct", header->key)) { + reqEvent = Event::fromXML(evhttp_decode_uri(header->value)); + break; + } else if (boost::iequals("_scxmleventname", header->key)) { + reqEvent.name = evhttp_decode_uri(header->value); + } else { + reqEvent.compound[header->key] = Data(evhttp_decode_uri(header->value), Data::VERBATIM); + } + } + + // get content into event + std::string content; + buf = evhttp_request_get_input_buffer(req); + while (evbuffer_get_length(buf)) { + int n; + char cbuf[128]; + n = evbuffer_remove(buf, cbuf, sizeof(buf)-1); + if (n > 0) { + content.append(cbuf, n); + } + } + reqEvent.compound["content"] = Data(content, Data::VERBATIM); + + EventIOProcessor* THIS = (EventIOProcessor*)arg; + THIS->_interpreter->receive(reqEvent); + + evhttp_send_reply(req, 200, "OK", NULL); +} + +void EventIOProcessor::httpSendReqDone(struct evhttp_request *req, void *cb_arg) { + if (req) { + LOG(INFO) << "got return code " << evhttp_request_get_response_code(req) << std::endl; + } +} + +void EventIOProcessor::invoke(InvokeRequest& req) { + +} +void EventIOProcessor::cancel(const std::string sendId) { + +} + +EventIOServer::EventIOServer(unsigned short port) { + _port = port; + _base = event_base_new(); + _http = evhttp_new(_base); + _handle = NULL; + while((_handle = evhttp_bind_socket_with_handle(_http, INADDR_ANY, _port)) == NULL) { + _port++; + } + determineAddress(); +} + +EventIOServer::~EventIOServer() { +} + +EventIOServer* EventIOServer::_instance = NULL; +tthread::recursive_mutex EventIOServer::_instanceMutex; + +EventIOServer* EventIOServer::getInstance() { + tthread::lock_guard<tthread::recursive_mutex> lock(_instanceMutex); + if (_instance == NULL) { + _instance = new EventIOServer(8080); + _instance->start(); + } + return _instance; +} + +void EventIOServer::registerProcessor(EventIOProcessor* processor) { + EventIOServer* THIS = getInstance(); + tthread::lock_guard<tthread::recursive_mutex> lock(THIS->_mutex); + + /** + * Determine path for interpreter. + * + * If the interpreter has a name and it is not yet taken, choose it as the path + * for requests. If the interpreters name path is already taken, append digits + * until we have an available path. + * + * If the interpreter does not specify a name, take its sessionid. + */ + + std::string path = processor->_interpreter->getName(); + if (path.size() == 0) { + path = processor->_interpreter->getSessionId(); + } + assert(path.size() > 0); + + std::stringstream actualPath(path); + int i = 1; + while(THIS->_processors.find(actualPath.str()) != THIS->_processors.end()) { + actualPath.str(std::string()); + actualPath.clear(); + actualPath << path << ++i; + } + + std::stringstream processorURL; + processorURL << "http://" << THIS->_address << ":" << THIS->_port << "/" << actualPath.str(); + + THIS->_processors[actualPath.str()] = processor; + processor->setURL(processorURL.str()); + + evhttp_set_cb(THIS->_http, ("/" + actualPath.str()).c_str(), EventIOProcessor::httpRecvReq, processor); +// evhttp_set_cb(THIS->_http, "/", EventIOProcessor::httpRecvReq, processor); +// evhttp_set_gencb(THIS->_http, EventIOProcessor::httpRecvReq, NULL); +} + +void EventIOServer::unregisterProcessor(EventIOProcessor* processor) { + EventIOServer* THIS = getInstance(); + tthread::lock_guard<tthread::recursive_mutex> lock(THIS->_mutex); + evhttp_del_cb(THIS->_http, processor->getURL().c_str()); +} + +void EventIOServer::start() { + _isRunning = true; + _thread = new tthread::thread(EventIOServer::run, this); +} + +void EventIOServer::run(void* instance) { + EventIOServer* THIS = (EventIOServer*)instance; + while(THIS->_isRunning) { + LOG(INFO) << "Dispatching HTTP Server" << std::endl; + event_base_dispatch(THIS->_base); + } + LOG(INFO) << "HTTP Server stopped" << std::endl; +} + +std::string EventIOServer::syncResolve(const std::string& hostname) { + struct hostent *he; + struct in_addr **addr_list; + int i; + + if ( (he = gethostbyname( hostname.c_str() ) ) != NULL) { + addr_list = (struct in_addr **) he->h_addr_list; + for(i = 0; addr_list[i] != NULL; i++) { + return std::string(inet_ntoa(*addr_list[i])); + } + } + return ""; +} + +void EventIOServer::determineAddress() { + + char hostname[1024]; + gethostname(hostname, 1024); + _address = std::string(hostname); + +#if 0 + struct sockaddr_storage ss; + evutil_socket_t fd; + ev_socklen_t socklen = sizeof(ss); + char addrbuf[128]; + + void *inaddr; + const char *addr; + int got_port = -1; + fd = evhttp_bound_socket_get_fd(_handle); + memset(&ss, 0, sizeof(ss)); + if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) { + perror("getsockname() failed"); + return; + } + + if (ss.ss_family == AF_INET) { + got_port = ntohs(((struct sockaddr_in*)&ss)->sin_port); + inaddr = &((struct sockaddr_in*)&ss)->sin_addr; + } else if (ss.ss_family == AF_INET6) { + got_port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port); + inaddr = &((struct sockaddr_in6*)&ss)->sin6_addr; + } else { + fprintf(stderr, "Weird address family %d\n", + ss.ss_family); + return; + } + addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf, + sizeof(addrbuf)); + if (addr) { + _address = std::string(addr); + } else { + fprintf(stderr, "evutil_inet_ntop failed\n"); + return; + } +#endif +} + +} +} +}
\ No newline at end of file diff --git a/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.h b/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.h new file mode 100644 index 0000000..7e8eaa9 --- /dev/null +++ b/src/uscxml/ioprocessor/basichttp/libevent/EventIOProcessor.h @@ -0,0 +1,96 @@ +#ifndef EVENTIOPROCESSOR_H_2CUY93KU +#define EVENTIOPROCESSOR_H_2CUY93KU + +#include "uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.h" +#include "uscxml/Interpreter.h" +#include "uscxml/Factory.h" +#include <sys/time.h> + +#include <event2/http.h> +#include <event2/http_struct.h> + +namespace uscxml { +namespace io { +namespace libevent { + +class EventIOServer; + +class EventIOProcessor : public uscxml::IOProcessor { +public: + struct SendData { + EventIOProcessor* ioProcessor; + uscxml::SendRequest req; + }; + + EventIOProcessor(); + virtual ~EventIOProcessor(); + virtual IOProcessor* create(uscxml::Interpreter* interpreter); + + virtual void send(uscxml::SendRequest& req); + virtual void invoke(uscxml::InvokeRequest& req); + virtual void cancel(const std::string sendId); + + std::string getURL() { return _url; } + void setURL(const std::string& url) { _url = url; } + + void start(); + static void run(void* instance); + + static void httpMakeSendReq(void* userdata, std::string eventName); + static void httpSendReqDone(struct evhttp_request *req, void *cb_arg); + static void httpRecvReq(struct evhttp_request *req, void *arg); + +protected: + std::map<std::string, SendData> _sendData; + + std::string _url; + + uscxml::DelayedEventQueue _eventQueue; + uscxml::Interpreter* _interpreter; + std::map<std::string, struct evhttp_connection*> _httpConnections; + std::map<std::string, struct evhttp_request*> _httpRequests; + struct evdns_base* _dns; + + friend class EventIOServer; +}; + +class EventIOServer { +private: + static EventIOServer* getInstance(); + EventIOServer(unsigned short port); + ~EventIOServer(); + + void start(); + void stop(); + static void run(void* instance); + + void determineAddress(); + static std::string syncResolve(const std::string& hostname); + + static void registerProcessor(EventIOProcessor* processor); + static void unregisterProcessor(EventIOProcessor* processor); + + + std::map<std::string, EventIOProcessor*> _processors; + + struct event_base* _base; + struct evhttp* _http; + struct evhttp_bound_socket* _handle; + + unsigned short _port; + std::string _address; + + static EventIOServer* _instance; + static tthread::recursive_mutex _instanceMutex; + tthread::thread* _thread; + tthread::recursive_mutex _mutex; + bool _isRunning; + + friend class EventIOProcessor; +}; + +} +} +} + +#endif /* end of include guard: EVENTIOPROCESSOR_H_2CUY93KU */
\ No newline at end of file diff --git a/src/uscxml/ioprocessor/basichttp/mongoose/MongooseIOProcessor.cpp b/src/uscxml/ioprocessor/basichttp/mongoose/MongooseIOProcessor.cpp new file mode 100644 index 0000000..a62fefc --- /dev/null +++ b/src/uscxml/ioprocessor/basichttp/mongoose/MongooseIOProcessor.cpp @@ -0,0 +1,3 @@ +#include "uscxml/ioprocessor/basichttp/mongoose/MongooseIOProcessor.h" +#include "uscxml/Message.h" + diff --git a/src/uscxml/ioprocessor/basichttp/mongoose/MongooseIOProcessor.h b/src/uscxml/ioprocessor/basichttp/mongoose/MongooseIOProcessor.h new file mode 100644 index 0000000..bb7a0fc --- /dev/null +++ b/src/uscxml/ioprocessor/basichttp/mongoose/MongooseIOProcessor.h @@ -0,0 +1,15 @@ +#ifndef MONGOOSEIOPROCESSOR_H_JS0GMSFO +#define MONGOOSEIOPROCESSOR_H_JS0GMSFO + +#include "uscxml/Interpreter.h" +#include "uscxml/Factory.h" + +namespace uscxml { + +class MongooseIOProcessor : public IOProcessor { + +}; + +} + +#endif /* end of include guard: MONGOOSEIOPROCESSOR_H_JS0GMSFO */ diff --git a/src/uscxml/ioprocessor/basichttp/pion/PionIOProcessor.cpp b/src/uscxml/ioprocessor/basichttp/pion/PionIOProcessor.cpp new file mode 100644 index 0000000..7aa9169 --- /dev/null +++ b/src/uscxml/ioprocessor/basichttp/pion/PionIOProcessor.cpp @@ -0,0 +1,74 @@ +#include "uscxml/ioprocessor/basichttp/pion/PionIOProcessor.h" +#include "uscxml/Message.h" + +#include <pion/http/request.hpp> +#include <pion/http/message.hpp> + +namespace uscxml { + +using namespace pion; + +PionIOProcessor::PionIOProcessor() { +} + +PionIOProcessor::~PionIOProcessor() { + +} + +IOProcessor* PionIOProcessor::create(Interpreter* interpreter) { + PionIOProcessor* io = new PionIOProcessor(); + io->_interpreter = interpreter; + io->_ioServer = PionIOServer::getInstance(); + return io; +} + +void handle_connection(pion::tcp::connection_ptr& tcp_conn) { +} + +void PionIOProcessor::send(SendRequest& req) { + + boost::system::error_code error_code; + boost::asio::io_service io_service; + + pion::tcp::connection tcp_conn(io_service, 0); + error_code = tcp_conn.connect("localhost", 8080); + if (error_code) throw error_code; // connection failed + + + + http::request httpReq; + httpReq.set_method("POST"); + if (req.event.size() > 0) + httpReq.add_header("_scxmleventname", req.event); + + httpReq.send(tcp_conn, error_code); + +// http::request_writer writer; +// writer. + +} +void PionIOProcessor::invoke(InvokeRequest& req) { + +} +void PionIOProcessor::cancel(const std::string sendId) { + +} + +PionIOServer::PionIOServer() : pion::tcp::server(0) { +} + +PionIOServer::~PionIOServer() { +} + +void PionIOServer::handle_connection(pion::tcp::connection_ptr& tcp_conn) { +} + +PionIOServer* PionIOServer::_instance = NULL; +PionIOServer* PionIOServer::getInstance() { + if (_instance == NULL) { + _instance = new PionIOServer(); + } + return _instance; +} + +}
\ No newline at end of file diff --git a/src/uscxml/ioprocessor/basichttp/pion/PionIOProcessor.h b/src/uscxml/ioprocessor/basichttp/pion/PionIOProcessor.h new file mode 100644 index 0000000..154acdb --- /dev/null +++ b/src/uscxml/ioprocessor/basichttp/pion/PionIOProcessor.h @@ -0,0 +1,47 @@ +#ifndef PIONIOPROCESSOR_H_VAITDNCN +#define PIONIOPROCESSOR_H_VAITDNCN + +#include "uscxml/Interpreter.h" +#include "uscxml/Factory.h" +#include "uscxml/concurrency/DelayedEventQueue.h" + +#include <pion/http/request_writer.hpp> +#include <pion/http/request_writer.hpp> +#include <pion/tcp/server.hpp> + +namespace uscxml { + +class PionIOServer : public pion::tcp::server { +public: + PionIOServer(); + virtual ~PionIOServer(); + DelayedEventQueue _eventQueue; + + virtual void handle_connection(pion::tcp::connection_ptr& tcp_conn); + + static PionIOServer* getInstance(); + static PionIOServer* _instance; + + pion::http::request_writer_ptr _writer; + pion::tcp::connection_ptr _conn; + +}; + +class PionIOProcessor : public IOProcessor { +public: + PionIOProcessor(); + virtual ~PionIOProcessor(); + virtual IOProcessor* create(Interpreter* interpreter); + + virtual void send(SendRequest& req); + virtual void invoke(InvokeRequest& req); + virtual void cancel(const std::string sendId); + +protected: + Interpreter* _interpreter; + PionIOServer* _ioServer; +}; + +} + +#endif /* end of include guard: PIONIOPROCESSOR_H_VAITDNCN */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..297428c --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,29 @@ +add_executable(test-predicates src/test-predicates.cpp) +target_link_libraries(test-predicates uscxml) +add_test(test-predicates ${CMAKE_SOURCE_DIR}/test/src/test-predicates.scxml) +set_target_properties(test-predicates PROPERTIES FOLDER "Tests") + +add_executable(test-execution src/test-execution.cpp) +target_link_libraries(test-execution uscxml) +add_test(test-execution ${CMAKE_SOURCE_DIR}/test/src/test-execution.scxml) +set_target_properties(test-execution PROPERTIES FOLDER "Tests") + +add_executable(test-apache-commons src/test-apache-commons.cpp) +target_link_libraries(test-apache-commons uscxml) +add_test(test-apache-commons ${CMAKE_SOURCE_DIR}/test/apache) +set_target_properties(test-apache-commons PROPERTIES FOLDER "Tests") + +add_executable(test-ecmascript-v8 src/test-ecmascript-v8.cpp) +target_link_libraries(test-ecmascript-v8 uscxml) +add_test(test-ecmascript-v8 test-ecmascript-v8) +set_target_properties(test-ecmascript-v8 PROPERTIES FOLDER "Tests") + +add_executable(test-communication src/test-communication.cpp) +target_link_libraries(test-communication uscxml) +add_test(test-communication test-communication) +set_target_properties(test-communication PROPERTIES FOLDER "Tests") + +add_executable(test-eventdelay src/test-eventdelay.cpp) +target_link_libraries(test-eventdelay uscxml) +add_test(test-eventdelay test-eventdelay) +set_target_properties(test-eventdelay PROPERTIES FOLDER "Tests") diff --git a/test/samples/apache/actions-initial-test.xml b/test/samples/apache/actions-initial-test.xml new file mode 100644 index 0000000..d2d3e48 --- /dev/null +++ b/test/samples/apache/actions-initial-test.xml @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initial="actionsTest"> + + <state id="actionsTest"> + <initial> + <transition target="child1"> + <cs:var name="foo"/> + <cs:var name="delta" expr="${1+2+3}" /> + <cs:var name="eventsent"/> + <if cond="${delta le 3}"> + <assign name="foo" expr="bar" /> + <elseif cond="${delta eq 3}"/> + <assign name="foo" expr="fubar" /> + <else/> + <assign name="foo" expr="foobar" /> + </if> + <cs:var name="drink" expr="water" /> + <cs:var name="eat" expr="flies" /> + <send sendid="send12345" target="freddy" type="frog" + event="croak" namelist="drink eat" hints="h2o bzz" + delay="${1000+500}" /> + <cancel sendId="send12345"/> + <log expr="leaving" label="entry001" /> + <event name="event.test"/> + <!-- exit will be ignored, makes little sense in initial --> + <cs:exit expr="later" namelist="freddy" /> + </transition> + </initial> + + <transition event="event.test"> + <assign name="eventsent" expr="true"/> + </transition> + + <state id="child1"/> + + </state> + +</scxml>
\ No newline at end of file diff --git a/test/samples/apache/actions-parallel-test.xml b/test/samples/apache/actions-parallel-test.xml new file mode 100644 index 0000000..be9d25c --- /dev/null +++ b/test/samples/apache/actions-parallel-test.xml @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initial="actionsTest"> + + <parallel id="actionsTest"> + <onentry> + <cs:var name="foo"/> + <cs:var name="delta" expr="${1+2+3}" /> + <cs:var name="eventsent"/> + <if cond="${delta le 3}"> + <assign name="foo" expr="bar" /> + <elseif cond="${delta eq 3}"/> + <assign name="foo" expr="fubar" /> + <else/> + <assign name="foo" expr="foobar" /> + </if> + <cs:var name="drink" expr="water" /> + <cs:var name="eat" expr="flies" /> + <send sendid="send12345" target="freddy" type="frog" + event="croak" namelist="drink eat" hints="h2o bzz" + delay="${1000+500}" /> + <cancel sendId="send12345"/> + <log expr="leaving" label="entry001" /> + <event name="event.test"/> + <cs:exit expr="later" namelist="freddy" /> + </onentry> + + <transition event="event.test"> + <assign name="eventsent" expr="true"/> + </transition> + + <!-- dummy regions --> + <state id="state01"/> + <state id="state02"/> + </parallel> + +</scxml>
\ No newline at end of file diff --git a/test/samples/apache/actions-state-test.xml b/test/samples/apache/actions-state-test.xml new file mode 100644 index 0000000..3374e21 --- /dev/null +++ b/test/samples/apache/actions-state-test.xml @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initialstate="actionsTest"> + + <state id="actionsTest" final="true"> + <onentry> + <cs:var name="foo"/> + <cs:var name="delta" expr="${1+2+3}" /> + <cs:var name="eventsent"/> + <if cond="${delta le 3}"> + <assign name="foo" expr="bar" /> + <elseif cond="${delta eq 3}"/> + <assign name="foo" expr="fubar" /> + <else/> + <assign name="foo" expr="foobar" /> + </if> + <cs:var name="drink" expr="water" /> + <cs:var name="eat" expr="flies" /> + <send sendid="send12345" target="freddy" type="frog" + event="croak" namelist="drink eat" hints="h2o bzz" + delay="${1000+500}" /> + <cancel sendId="send12345"/> + <log expr="leaving" label="entry001" /> + <event name="event.test"/> + <cs:exit expr="later" namelist="freddy" /> + </onentry> + + <transition event="event.test"> + <assign name="eventsent" expr="true"/> + </transition> + + </state> + +</scxml>
\ No newline at end of file diff --git a/test/samples/apache/assign-test-01.xml b/test/samples/apache/assign-test-01.xml new file mode 100644 index 0000000..ed7e977 --- /dev/null +++ b/test/samples/apache/assign-test-01.xml @@ -0,0 +1,70 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Test "src" attribute of assign element --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:rad="http://foo/bar" + version="1.0" + initialstate="assign1"> + + <state id="assign1" final="true"> + + <datamodel> + <data id="foo"> + <root xmlns=""> + <foo/> + </root> + </data> + <data id="bar"> + <root xmlns=""> + <bar>5</bar> + </root> + </data> + </datamodel> + + <onentry> + <assign location="Data(foo,'root/foo')" src="assign-src.xml"/> + </onentry> + + <transition cond="Data(foo,'root/foo/a') + Data(bar,'root/bar') eq 15" + target="assign2" /> + + </state> + + <state id="assign2"> + + <datamodel> + <data id="jira51data1"> + <rad:timeout>10</rad:timeout> + </data> + <data id="jira51data2"> + <rad:short xmlns="">20</rad:short> + </data> + </datamodel> + + <onentry> + <assign location="Data(jira51data1,'rad:timeout')" expr="Data(jira51data2,'rad:short')"/> + </onentry> + + <transition cond="Data(jira51data1,'rad:timeout') eq 20" + target="assign3" /> + + </state> + + <state id="assign3" final="true"/> + +</scxml> diff --git a/test/samples/apache/assign-test-02.xml b/test/samples/apache/assign-test-02.xml new file mode 100644 index 0000000..38ab9b3 --- /dev/null +++ b/test/samples/apache/assign-test-02.xml @@ -0,0 +1,46 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Regress JIRA 89, incomplete child removal --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" xmlns:rad="http://foo/bar" version="1.0" initialstate="assign1"> + <datamodel> + <data name="source"> + <rad:foo> + <rad:a>1</rad:a> + <rad:b>2</rad:b> + </rad:foo> + </data> + <data name="destination"> + <rad:bar> + <rad:a>3</rad:a> + <rad:b>4</rad:b> + </rad:bar> + </data> + </datamodel> + <!-- verify the destination contents --> + <state id="assign1" final="true"> + <transition cond="Data(destination,'rad:bar/rad:a') eq 3 and Data(destination,'rad:bar/rad:b') eq 4" target="assign2" /> + </state> + <!-- copy the new contents and verify --> + <state id="assign2"> + <onentry> + <assign location="Data(destination,'rad:bar')" expr="Data(source,'rad:foo')" /> + </onentry> + <transition cond="Data(destination,'rad:bar/rad:a') eq 1 and Data(destination,'rad:bar/rad:b') eq 2" target="assign3" /> + </state> + <state id="assign3" final="true" /> +</scxml> diff --git a/test/samples/apache/bar.xml b/test/samples/apache/bar.xml new file mode 100644 index 0000000..3901080 --- /dev/null +++ b/test/samples/apache/bar.xml @@ -0,0 +1,25 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="bar"> + + <state id="bar"/> + +</scxml> + diff --git a/test/samples/apache/custom-hello-world-01.xml b/test/samples/apache/custom-hello-world-01.xml new file mode 100644 index 0000000..5d8a348 --- /dev/null +++ b/test/samples/apache/custom-hello-world-01.xml @@ -0,0 +1,37 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for comparison with hello-world.xml by + CustomActionTest.java in model package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:my="http://my.custom-actions.domain/CUSTOM1" + xmlns:foo="http://my.custom-actions.domain/CUSTOM2" + version="1.0" + initialstate="custom"> + + <state id="custom" final="true"> + + <onentry> + <my:hello name="world" /> + <!-- foo:bar also maps to Hello action --> + <foo:bar name="custom action" /> + </onentry> + + </state> + +</scxml> + diff --git a/test/samples/apache/custom-hello-world-02.xml b/test/samples/apache/custom-hello-world-02.xml new file mode 100644 index 0000000..7b27ccf --- /dev/null +++ b/test/samples/apache/custom-hello-world-02.xml @@ -0,0 +1,33 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for CustomActionTest.java in model package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:my="http://my.custom-actions.domain/CUSTOM" + version="1.0" + initialstate="custom"> + + <state id="custom" final="true"> + + <onentry> + <my:hello name="child (included) document" /> + </onentry> + + </state> + +</scxml> + diff --git a/test/samples/apache/custom-hello-world-03.xml b/test/samples/apache/custom-hello-world-03.xml new file mode 100644 index 0000000..c8cfc5e --- /dev/null +++ b/test/samples/apache/custom-hello-world-03.xml @@ -0,0 +1,33 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for CustomActionTest.java in model package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:my="http://my.custom-actions.domain/CUSTOM" + version="1.0" + initialstate="custom"> + + <state id="custom" final="true"> + + <onentry> + <my:send name="overridden local name" /> + </onentry> + + </state> + +</scxml> + diff --git a/test/samples/apache/custom-hello-world-04-el.xml b/test/samples/apache/custom-hello-world-04-el.xml new file mode 100644 index 0000000..0d5300b --- /dev/null +++ b/test/samples/apache/custom-hello-world-04-el.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for CustomActionTest.java in model package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:my="http://my.custom-actions.domain/CUSTOM" + version="1.0" initialstate="init"> + <datamodel> + <data id="helloName1" /> + </datamodel> + + <state id="init"> + <onentry> + <my:hello name="custom04" /> + </onentry> + + <transition event="helloevent" target="middle1"> + <assign name="helloName1" expr="${_eventdatamap.helloevent}" /> + </transition> + </state> + + <state id="middle1"> + <transition target="custom" cond="${helloName1 eq 'custom04'}" /> + </state> + + <state id="custom" final="true"/> + +</scxml> + + diff --git a/test/samples/apache/custom-hello-world-04-jexl.xml b/test/samples/apache/custom-hello-world-04-jexl.xml new file mode 100644 index 0000000..c44e9b3 --- /dev/null +++ b/test/samples/apache/custom-hello-world-04-jexl.xml @@ -0,0 +1,62 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for CustomActionTest.java in model package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:my="http://my.custom-actions.domain/CUSTOM" + version="1.0" initialstate="init"> + <datamodel> + <data id="helloName1" /> + </datamodel> + + <state id="init"> + <onentry> + <my:hello name="custom04a" /> + </onentry> + + <transition event="helloevent" target="middle1"> + <assign name="helloName1" expr="_eventdatamap['helloevent']" /> + </transition> + </state> + + <state id="middle1"> + <transition target="custom1" cond="helloName1 eq 'custom04a'" /> + </state> + + <state id="custom1"> + <transition event="custom.next" target="custom2"/> + </state> + + <state id="custom2"> + <onentry> + <my:hello name="custom04b" /> + </onentry> + + <transition event="helloevent" target="custom3"> + <assign name="helloName1" expr="_eventdatamap.helloevent" /> + </transition> + </state> + + <state id="custom3"> + <transition target="end" cond="helloName1 eq 'custom04b'" /> + </state> + + <state id="end" final="true"/> + +</scxml> + + diff --git a/test/samples/apache/datamodel-01.xml b/test/samples/apache/datamodel-01.xml new file mode 100644 index 0000000..cc874fb --- /dev/null +++ b/test/samples/apache/datamodel-01.xml @@ -0,0 +1,107 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- A fictitious state machine used by test cases. + Meant to illustrate the usage of SCXML <datamodel> element + and the Commons SCXML Data() function --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="main"> + + <!-- Root or document datamodel --> + <datamodel> + <data id="docdata"> + <root xmlns=""> + <foo>foo</foo> + </root> + </data> + </datamodel> + + <state id="main"> + + <initial> + <transition target="ten"/> + </initial> + + <!-- datamodel scoped to state "main" --> + <datamodel> + <data id="mainvar" expr="${0}" /> + <data id="maindata"> + <root xmlns=""> + <foo> + <bar>bar</bar> + </foo> + </root> + </data> + </datamodel> + + <state id="ten"> + <onentry> + <!-- Assign Usage 1: name is previously defined + <var> or degenerate <data> (as in this case) --> + <assign name="mainvar" expr="${10}" /> + </onentry> + <!-- Commons SCXML defines a Data() function to use in conjunction + with the Commons EL expression language. The + first argument is the named XML data tree and the second is + the XPath expression to a node whose value is to be + examined --> + <transition event="ten.done" + cond="${mainvar eq 10 and Data(maindata,'root/foo/bar') eq 'bar'}" + target="twenty" /> + <onexit> + <!-- Assign Usage 2: location must point to an existing + node --> + <assign location="${Data(maindata,'root/foo/bar')}" expr="baz" /> + </onexit> + </state> + + <state id="twenty"> + <onentry> + <assign name="mainvar" expr="${20}" /> + </onentry> + <transition event="twenty.done" + cond="${Data(maindata,'root/foo/bar') eq 'baz' and mainvar eq 20}" + target="thirty" /> + <onexit> + <!-- Assign Usage 3: location points to an existing + node, and expr points to an existing node. + In this case, location adopts expr's child nodes. --> + <assign location="${Data(docdata,'root/foo')}" + expr="${Data(maindata,'root/foo')}" /> + <assign location="${Data(docdata,'root/foo/bar')}" + expr="${10}" /> + </onexit> + </state> + + <state id="thirty"> + <!-- Arithmetic operations are possible with results from + the Data() function. Note that data "docdata" + did not have a node at 'root/foo/bar' to begin with, + the XML tree was manipulated by the <assign> above --> + <transition event="thirty.done" + cond="${Data(docdata,'root/foo/bar') gt 5}" + target="forty" /> + </state> + + <state id="forty" final="true" /> + + </state> + +</scxml> + + diff --git a/test/samples/apache/datamodel-02.xml b/test/samples/apache/datamodel-02.xml new file mode 100644 index 0000000..b719b8b --- /dev/null +++ b/test/samples/apache/datamodel-02.xml @@ -0,0 +1,83 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- A fictitious state machine used by test cases. Meant to illustrate + prefixed XPath expressions in the Commons SCXML Data() function --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="ten"> + + <!-- Start with a prefixless XPath (see transition cond) --> + <state id="ten"> + + <datamodel> + <data id="data10"> + <root xmlns=""> + <foo>10</foo> + </root> + </data> + </datamodel> + + <transition event="ten.done" cond="${Data(data10,'root/foo') eq 10}" + target="twenty" /> + + </state> + + <!-- Already defined (and identical) prefixes --> + <state id="twenty" xmlns:ns1="http://namespace.test.domain/1" + xmlns:ns2="http://namespace.test.domain/2"> + + <datamodel> + <!-- Start with a prefixless XPath --> + <data id="data20"> + <ns1:root> + <ns2:foo>20</ns2:foo> + </ns1:root> + </data> + </datamodel> + + <transition event="twenty.done" cond="${Data(data20,'ns1:root/ns2:foo') eq 20}" + target="thirty" /> + + </state> + + <!-- Data without prefixes --> + <state id="thirty"> + + <datamodel> + <!-- Start with a prefixless XPath --> + <data id="data30"> + <root xmlns="http://namespace.test.domain/1"> + <foo xmlns="http://namespace.test.domain/2">30</foo> + </root> + </data> + </datamodel> + + <transition event="thirty.done" + xmlns:ns1="http://namespace.test.domain/1" + xmlns:ns2="http://namespace.test.domain/2" + xmlns:ns3="http://namespace.test.domain/3" + cond="${Data(data30,'ns1:root/ns2:foo') eq 30}" + target="forty" /> + + </state> + + <state id="forty" final="true" /> + +</scxml> + + diff --git a/test/samples/apache/datamodel-03.xml b/test/samples/apache/datamodel-03.xml new file mode 100644 index 0000000..f8a3310 --- /dev/null +++ b/test/samples/apache/datamodel-03.xml @@ -0,0 +1,259 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- A fictitious state machine used by test cases. Meant to illustrate + prefixed XPath expressions in the Commons SCXML Data() function. + Used by org.apache.commons.scxml.NamespacePrefixedPathsTest + Also serves as testing the underlying functionality of the + underlying parsing technology (here, Digester 1.8) --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initialstate="ten"> + + <!-- Root data model --> + <datamodel> + + <!-- We'll use this for XPaths --> + <data id="rootdata" + xmlns:ns1="scheme://namespace.test.domain/1" + xmlns:ns2="scheme://namespace.test.domain/2" + xmlns:ns3="scheme://namespace.test.domain/3"> + <ns1:root> + <ns2:foo> + <ns3:bar>1</ns3:bar> + </ns2:foo> + </ns1:root> + </data> + + <!-- We'll use this for the JUnit test + NamespacePrefixedPathsTest.java (scxml package) --> + <data id="retval" /> + + </datamodel> + + <!-- State data model --> + <state id="ten"> + + <datamodel> + + <data id="data10" xmlns:ns4="scheme://namespace.test.domain/1"> + <root xmlns=""> + <ns4:foo> + <bar>10</bar> + </ns4:foo> + </root> + </data> + + </datamodel> + + <onentry> + <cs:var xmlns:pre1="scheme://namespace.test.domain/1" + xmlns:pre2="scheme://namespace.test.domain/2" + xmlns:pre3="scheme://namespace.test.domain/3" + name="tentest" + expr="${Data(rootdata,'pre1:root/pre2:foo/pre3:bar') + Data(data10,'root/pre1:foo/bar')}" /> + </onentry> + + <transition event="ten.done" + cond="${tentest eq 11}" + target="twenty" /> + + <onexit> + <assign name="retval" + expr="${tentest}" /> + </onexit> + + </state> + + <!-- Already defined (and different) prefixes --> + <state id="twenty" xmlns:ns1="scheme://namespace.test.domain/1" + xmlns:ns2="scheme://namespace.test.domain/2" + xmlns:ns3="scheme://namespace.test.domain/3"> + + <datamodel> + + <data id="data20"> + <ns1:root> + <ns2:foo>20</ns2:foo> + </ns1:root> + </data> + + </datamodel> + + <onentry> + <assign location="${Data(rootdata,'ns1:root/ns2:foo/ns3:bar')}" + expr="${2}" /> + </onentry> + + + <!-- Redefine namespace prefixes --> + <transition event="twenty.done" + xmlns:ns1="scheme://namespace.test.domain/1" + xmlns:ns2="scheme://namespace.test.domain/2" + cond="${Data(data20,'ns1:root/ns2:foo') eq 20 and Data(rootdata,'ns1:root/ns2:foo/ns3:bar') eq 2}" + target="thirty" /> + + <onexit> + + <!-- Redefine different prefixes bound to above namespaces --> + <if xmlns:pre1="scheme://namespace.test.domain/1" + xmlns:pre2="scheme://namespace.test.domain/2" + cond="${Data(data20,'pre1:root/pre2:foo') lt 20}"> + + <assign name="retval" expr="Less than 20" /> + + <elseif cond="${Data(data20,'pre1:root/pre2:foo') eq 20}" /> + + <assign name="retval" expr="Equal to 20" /> + + <else/> + + <assign name="retval" expr="Greater than 20" /> + + </if> + + </onexit> + + </state> + + <!-- XPath looking at attribute --> + <state id="thirty"> + + <datamodel> + + <data id="data30"> + <root xmlns="scheme://namespace.test.domain/1"> + <foo xmlns="scheme://namespace.test.domain/2" + xmlns:ns1="scheme://namespace.test.domain/3" + ns1:attfoo="30" attbar="300"/> + </root> + </data> + + </datamodel> + + <transition event="thirty.done" + xmlns:ns1="scheme://namespace.test.domain/1" + xmlns:ns2="scheme://namespace.test.domain/2" + xmlns:ns3="scheme://namespace.test.domain/3" + cond="${Data(data30,'ns1:root/ns2:foo/@ns3:attfoo') + Data(data30,'ns1:root/ns2:foo/@attbar') eq 330}" + target="forty" /> + + </state> + + <!-- Multiple data, already defined prefixes --> + <state id="forty" xmlns:ns1="scheme://namespace.test.domain/1" + xmlns:ns2="scheme://namespace.test.domain/2" + xmlns:ns3="scheme://namespace.test.domain/3" + xmlns:ns4="scheme://namespace.test.domain/4"> + + <datamodel> + + <data id="data40"> + <root xmlns=""> + <ns1:foo ns2:attfoo="40"/> + </root> + </data> + + <data id="data41"> + <ns3:root> + <ns4:foo>41</ns4:foo> + </ns3:root> + </data> + + </datamodel> + + <transition event="forty.done" + cond="${Data(data40,'root/ns1:foo/@ns2:attfoo') + Data(data41,'ns3:root/ns4:foo') eq 81}" + target="fifty" /> + + </state> + + <!-- Multiple data, prefixes on elements --> + <state id="fifty"> + + <datamodel> + + <data id="data50" xmlns:ns1="scheme://namespace.test.domain/1" + xmlns:ns2="scheme://namespace.test.domain/2" + xmlns:ns3="scheme://namespace.test.domain/3"> + <ns1:root> + <ns2:foo ns3:attfoo="50"/> + </ns1:root> + </data> + + <data id="data51" xmlns:ns3="scheme://namespace.test.domain/3" + xmlns:ns4="scheme://namespace.test.domain/4"> + <ns3:root> + <ns4:foo attfoo="51"/> + </ns3:root> + </data> + + </datamodel> + + <transition event="fifty.done" + xmlns:ns1="scheme://namespace.test.domain/1" + xmlns:ns2="scheme://namespace.test.domain/2" + xmlns:ns3="scheme://namespace.test.domain/3" + xmlns:ns4="scheme://namespace.test.domain/4" + cond="${Data(data50,'ns1:root/ns2:foo/@ns3:attfoo') + Data(rootdata,'ns1:root/ns2:foo/ns3:bar') eq 52}" + target="sixty" /> + + </state> + + <!-- Multiple data, prefixes on datamodel and transition elements --> + <state id="sixty"> + + <datamodel xmlns:ns1="scheme://namespace.test.domain/1" + xmlns:ns2="scheme://namespace.test.domain/2" + xmlns:ns3="scheme://namespace.test.domain/3" + xmlns:ns4="scheme://namespace.test.domain/4"> + + <data id="data60"> + <root xmlns=""> + <ns1:foo ns2:attfoo="60"/> + </root> + </data> + + <data id="data61"> + <ns3:root> + <ns4:foo attfoo="61"/> + </ns3:root> + </data> + + </datamodel> + + <transition event="sixty.done" + xmlns:pre1="scheme://namespace.test.domain/1" + xmlns:pre2="scheme://namespace.test.domain/2" + xmlns:pre3="scheme://namespace.test.domain/3" + xmlns:pre4="scheme://namespace.test.domain/4" + cond="${Data(data60,'root/pre1:foo/@pre2:attfoo') + Data(data61,'pre3:root/pre4:foo/@attfoo') eq 121}" + target="seventy"> + + <!-- should be 121 --> + <log expr="${Data(data60,'root/pre1:foo/@pre2:attfoo') + Data(data61,'pre3:root/pre4:foo/@attfoo')}"/> + + </transition> + + </state> + + <state id="seventy" final="true" /> + +</scxml> + + diff --git a/test/samples/apache/datamodel-04.xml b/test/samples/apache/datamodel-04.xml new file mode 100644 index 0000000..503a858 --- /dev/null +++ b/test/samples/apache/datamodel-04.xml @@ -0,0 +1,48 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- A fictitious state machine used by test cases. + Meant to illustrate the usage of SCXML <datamodel> element + to persist some _eventdata --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="ten"> + + <!-- Root or document datamodel --> + <datamodel> + <data id="payload"/> + </datamodel> + + <state id="ten"> + <transition event="ten.done" target="twenty"> + <assign name="payload" expr="_eventdata" /> + </transition> + </state> + + <state id="twenty"> + <transition event="twenty.done" target="thirty" /> + <onexit> + <log label="Persisted eventdata.one" expr="payload.one"/> + <log label="Persisted eventdata.two" expr="payload.two"/> + </onexit> + </state> + + <state id="thirty" final="true"/> + +</scxml> + + diff --git a/test/samples/apache/edit-profile-config.xml b/test/samples/apache/edit-profile-config.xml new file mode 100644 index 0000000..cdab498 --- /dev/null +++ b/test/samples/apache/edit-profile-config.xml @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + $Id: edit-profile-config.xml 462838 2006-10-11 16:08:36Z rahul $ + +--> + +<!-- + + Dialog definitions for Shale Use Cases Example Web Application + written out as SCXML to demonstrate use of Commons SCXML as one + of Shale's Dialog Manager implementations. + + Related artifacts from <dialog name="Edit Profile">...</dialog> + in original dialogs definition file from Shale nightlies. + +--> + +<scxml xmlns="http://www.w3.org/2005/01/SCXML" version="1.0" + initialstate="edit"> + + <state id="edit"> + + <initial> + <transition> + <target next="setup"/> + </transition> + </initial> + + <!-- global transitions (within state "edit") --> + + <transition event="faces.outcome" + cond="${outcome eq 'cancel'}"> + <target next="cancel"/> + </transition> + + <transition event="faces.outcome" + cond="${outcome eq 'finish'}"> + <target next="finish"/> + </transition> + + <state id="setup"> + + <onentry> + <var name="setupOutcome" + expr="#{profile$edit.setup}" /> + </onentry> + + <transition cond="${setupOutcome eq 'success'}"> + <target next="page1"/> + </transition> + + </state> + + <state id="page1"> + + <transition event="faces.outcome" + cond="${outcome eq 'next'}"> + <target next="page2"/> + </transition> + + </state> + + <state id="page2"> + + <transition event="faces.outcome" + cond="${outcome eq 'previous'}"> + <target next="page1"/> + </transition> + + <transition event="faces.outcome" + cond="${outcome eq 'next'}"> + <target next="page3"/> + </transition> + + </state> + + <state id="page3"> + + <transition event="faces.outcome" + cond="${outcome eq 'previous'}"> + <target next="page2"/> + </transition> + + <transition event="faces.outcome" + cond="${outcome eq 'next'}"> + <target next="editExit"/> + </transition> + + </state> + + </state> + + <state id="cancel"> + + <onentry> + <var name="cancelOutcome" + expr="#{profile$edit.cancel}" /> + </onentry> + + <transition cond="${cancelOutcome eq 'success'}"> + <var name="outcome" + expr="cancel"/> + <target next="editExit"/> + </transition> + + </state> + + <state id="finish"> + + <onentry> + <var name="finishOutcome" + expr="#{profile$edit.finish}" /> + </onentry> + + <transition cond="${finishOutcome eq 'username'}"> + <target next="page1"/> + </transition> + + <transition cond="${finishOutcome eq 'password'}"> + <target next="page1"/> + </transition> + + <transition cond="${finishOutcome eq 'success'}"> + <var name="outcome" + expr="success"/> + <target next="editExit"/> + </transition> + + </state> + + <state id="editExit" + final="true" /> + +</scxml> diff --git a/test/samples/apache/eventdata-01.xml b/test/samples/apache/eventdata-01.xml new file mode 100644 index 0000000..c07d42d --- /dev/null +++ b/test/samples/apache/eventdata-01.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + datamodel="ecmascript" + initialstate="state1"> + <state id="state1"> + <transition event="event.foo" cond="_event.data == 2" + target="state2"/> + <transition event="event.foo" cond="_event.data == 3" + target="state3"/> + <transition event="event.foo"> + <log expr="'event.name: ' + _event.name" /> + </transition> + </state> + <state id="state2" final="true"/> + <state id="state3"> + <transition event="event.bar" target="state4" + cond="_event.data == 4"/> + <transition event="event.bar" target="state5" + cond="_event.data == 5"/> + <transition event="event.bar" target="state6" + cond="_event.data == 6"/> + </state> + <state id="state4" final="true"/> + <state id="state5" final="true"/> + <state id="state6"> + <transition event="event.baz" target="state7" + cond="_event.data == 7"/> + </state> + <state id="state7" final="true"/> +</scxml> diff --git a/test/samples/apache/eventdata-02.xml b/test/samples/apache/eventdata-02.xml new file mode 100644 index 0000000..df130ae --- /dev/null +++ b/test/samples/apache/eventdata-02.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + datamodel="ecmascript" + initialstate="state0"> + <state id="state0"> + <!-- Payload can be any object, such as a String ... --> + <transition event="connection.alerting" + cond="_event.data == 'line1'" target="state1"/> + <transition event="connection.alerting" + cond="_event.data == 'line2'" target="state2"/> + </state> + <state id="state1" final="true"/> + <state id="state2"> + <!-- ... or an arbitrary, user defined object. --> + <transition event="connection.alerting" + cond="_event.data.line == 3" target="state3"/> + <transition event="connection.alerting" + cond="_event.data.line == 4" target="state4"/> + </state> + <state id="state3" final="true"/> + <state id="state4" final="true"/> +</scxml> diff --git a/test/samples/apache/eventdata-03.xml b/test/samples/apache/eventdata-03.xml new file mode 100644 index 0000000..d4c4388 --- /dev/null +++ b/test/samples/apache/eventdata-03.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initialstate="ten"> + + <datamodel> + <data id="rootdata"> + <root xmlns=""> + <one>1</one> + <two>2</two> + </root> + </data> + </datamodel> + + <state id="ten"> + <transition event="event.foo" target="twenty"/> + </state> + + <state id="twenty"> + <onentry> + <cs:var name="one" expr="Data(rootdata,'root/one')"/> + <cs:var name="two" expr="Data(rootdata,'root/two')"/> + <send event="'event.bar'" namelist="one two"/> + </onentry> + <transition event="event.bar" + cond="_eventdatamap['event.bar'].one + _eventdatamap['event.bar'].two eq 3" + target="thirty"/> + </state> + + <state id="thirty" final="true"/> + +</scxml> diff --git a/test/samples/apache/eventdata-04.xml b/test/samples/apache/eventdata-04.xml new file mode 100644 index 0000000..541d2e9 --- /dev/null +++ b/test/samples/apache/eventdata-04.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="start"> + + <parallel id="start"> + + <state id="simulatedUser"> + <datamodel> + <data name="one" expr="1" /> + <data name="two" expr="2" /> + </datamodel> + + <onentry> + <send event="'event.bar'" namelist="one two" delay="'100ms'"/> + </onentry> + <transition event="event.bar"> + <log label="'simulatedUser'" expr="_eventdatamap['event.bar'].one + ', ' + _eventdatamap['event.bar'].two"/> + </transition> + </state> + + <state id="counter"> + <initial> + <transition target="twenty"/> + </initial> + + <state id="twenty"> + <transition event="event.bar" + cond="_eventdatamap['event.bar'].one + _eventdatamap['event.bar'].two eq 3" + target="thirty"/> + <transition event="event.bar"> + <log label="'event.bar in twenty state'" expr="_eventdatamap['event.bar'].one + ', ' + _eventdatamap['event.bar'].two"/> + </transition> + </state> + + <state id="thirty" final="true"/> + </state> + + </parallel> + +</scxml> diff --git a/test/samples/apache/external-hello-world.xml b/test/samples/apache/external-hello-world.xml new file mode 100644 index 0000000..b6d1b9f --- /dev/null +++ b/test/samples/apache/external-hello-world.xml @@ -0,0 +1,33 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for testing custom actions in external document pulled in via + the src attributes by ExternalCustomActionTest.java in model package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:my="http://my.custom-actions.domain/CUSTOM" + version="1.0" + initialstate="external-hello"> + + <state id="external-hello" final="true" src="custom-hello-world-02.xml"> + + <onentry> + <my:hello name="parent document" /> + </onentry> + + </state> + +</scxml> diff --git a/test/samples/apache/foo.xml b/test/samples/apache/foo.xml new file mode 100644 index 0000000..486d36e --- /dev/null +++ b/test/samples/apache/foo.xml @@ -0,0 +1,25 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="foo"> + + <state id="foo"/> + +</scxml> + diff --git a/test/samples/apache/hello-world.xml b/test/samples/apache/hello-world.xml new file mode 100644 index 0000000..7647899 --- /dev/null +++ b/test/samples/apache/hello-world.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for comparison with custom-hello-world.xml by + CustomActionTest.java in model package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="hello"> + + <state id="hello" final="true"> + <onentry> + <log expr="'hello world'" /> + </onentry> + </state> + +</scxml> + diff --git a/test/samples/apache/history-deep-01.xml b/test/samples/apache/history-deep-01.xml new file mode 100644 index 0000000..209a974 --- /dev/null +++ b/test/samples/apache/history-deep-01.xml @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + This document is an example of using deep history +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" + initialstate="flow"> + + <state id="flow"> + <initial> + <transition target="phases"/> + </initial> + + <!-- deep history is specified by setting the type attribute + to "deep" --> + + <history id="hist" type="deep"> + + <!-- This is the transition to be followed if no + prior history is available --> + + <transition target="phases"/> + + </history> + + <state id="phases"> + + <initial> + <transition target="phase1"/> + </initial> + + <state id="phase1"> + <transition event="phase.done" target="phase2"/> + </state> + + <state id="phase2"> + <transition event="phase.done" target="phase3"/> + </state> + + <state id="phase3" final="true" /> + + </state> + + <transition event="flow.pause" target="interrupted"/> + + <transition event="flow.terminate" target="terminated"/> + + </state> + + <state id="interrupted"> + + <transition event="flow.resume" target="hist"/> + + <transition event="flow.terminate" target="terminated"/> + + </state> + + <state id="terminated" final="true"/> + +</scxml> diff --git a/test/samples/apache/history-default-01.xml b/test/samples/apache/history-default-01.xml new file mode 100644 index 0000000..cf2f70f --- /dev/null +++ b/test/samples/apache/history-default-01.xml @@ -0,0 +1,90 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + This document is an example of specifying default transitions for + history states (if the parent state has never been visited before) +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" + initialstate="state1"> + + <state id="state1"> + <initial> + <transition target="history1"/> + </initial> + + <!-- shallow history, explicit default transition --> + + <history id="history1"> + <transition next="state11"/> + </history> + + <state id="state11"> + <transition event="state.next" target="state2"/> + </state> + + </state> + + <state id="state2"> + <initial> + <transition target="history2"/> + </initial> + + <!-- deep history, explicit default transition --> + + <history id="history2" type="deep"> + <transition next="state211"/> + </history> + + <state id="state21"> + + <initial> + <transition target="state212"/> + </initial> + + <state id="state211"> + <transition event="state.next" target="history3"/> + </state> + + <state id="state212"> + <transition event="state.next" target="history3"/> + </state> + + </state> + + </state> + + <state id="state3"> + + <initial> + <transition target="state31"/> + </initial> + + <!-- shallow history, no default transition specified, + reuse initial as default transition --> + + <history id="history3"/> + + <state id="state31"> + <transition event="state.next" target="state4"/> + </state> + + </state> + + <state id="state4" final="true"/> + +</scxml> diff --git a/test/samples/apache/history-shallow-01.xml b/test/samples/apache/history-shallow-01.xml new file mode 100644 index 0000000..20b5bb5 --- /dev/null +++ b/test/samples/apache/history-shallow-01.xml @@ -0,0 +1,67 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + This document is an example of using shallow history +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" + initialstate="flow"> + + <state id="flow"> + <initial> + <transition target="phase1"/> + </initial> + + <!-- history defaults to shallow, optionally one can set + the type attribute to "shallow" for identical results --> + + <history id="hist"> + + <!-- This is the transition to be followed if no + prior history is available --> + + <transition target="phase1"/> + + </history> + + <state id="phase1"> + <transition event="phase.done" target="phase2"/> + </state> + + <state id="phase2"> + <transition event="phase.done" target="phase3"/> + </state> + + <state id="phase3" final="true"/> + + <transition event="flow.pause" target="interrupted"/> + + <transition event="flow.terminate" target="terminated"/> + + </state> + + <state id="interrupted"> + + <transition event="flow.resume" target="hist"/> + + <transition event="flow.terminate" target="terminated"/> + + </state> + + <state id="terminated" final="true"/> + +</scxml> diff --git a/test/samples/apache/invoked-01.xml b/test/samples/apache/invoked-01.xml new file mode 100644 index 0000000..a523995 --- /dev/null +++ b/test/samples/apache/invoked-01.xml @@ -0,0 +1,32 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="state1"> + + <state id="state1"> + <onentry> + <log expr="'foo is ' + foo + ' and bar is ' + bar" /> + </onentry> + <transition event="invoked.next" target="state2" /> + </state> + + <state id="state2" final="true" /> + +</scxml> + diff --git a/test/samples/apache/invoked-02.xml b/test/samples/apache/invoked-02.xml new file mode 100644 index 0000000..b1b471a --- /dev/null +++ b/test/samples/apache/invoked-02.xml @@ -0,0 +1,32 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="state1"> + + <state id="state1"> + <onentry> + <send event="'invoked.next'" /> + </onentry> + <transition event="invoked.next" target="state2" /> + </state> + + <state id="state2" final="true" /> + +</scxml> + diff --git a/test/samples/apache/invoked-03-01.xml b/test/samples/apache/invoked-03-01.xml new file mode 100644 index 0000000..3b73a66 --- /dev/null +++ b/test/samples/apache/invoked-03-01.xml @@ -0,0 +1,33 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used by InvokeTest#testInvoke03Sample() --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="s1"> + + <state id="s1"> + <onentry> + <log expr="' Inner invoke ...'"/> + </onentry> + <transition event="s1.next" target="s2"/> + </state> + + <state id="s2" final="true"/> + +</scxml> + diff --git a/test/samples/apache/invoked-03.xml b/test/samples/apache/invoked-03.xml new file mode 100644 index 0000000..91244e8 --- /dev/null +++ b/test/samples/apache/invoked-03.xml @@ -0,0 +1,37 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used by InvokeTest#testInvoke03Sample() --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="state1"> + + <state id="state1"> + <onentry> + <log expr="' Outer invoke ...'"/> + </onentry> + <invoke type="scxml" src="invoked-03-01.xml"/> + <transition event="state1.next" target="end1" /> + </state> + + <state id="end1" final="true"> + <onentry> + <log expr="' Inner invoke completed'"/> + </onentry> + </state> + +</scxml> diff --git a/test/samples/apache/invoker-01.xml b/test/samples/apache/invoker-01.xml new file mode 100644 index 0000000..5355896 --- /dev/null +++ b/test/samples/apache/invoker-01.xml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="invoker"> + + <state id="invoker"> + <onentry> + <log expr="'Begin invoke test ...'" /> + </onentry> + <invoke type="scxml" src="invoked-01.xml"> + <param name="foo" expr="'foo'" /> + <param name="bar" expr="'bar'" /> + <finalize> + <log expr="'Finalizing ...'" /> + </finalize> + </invoke> + <transition event="invoker.invoke.done" target="end" /> + </state> + + <state id="end" final="true" /> + +</scxml> diff --git a/test/samples/apache/invoker-02.xml b/test/samples/apache/invoker-02.xml new file mode 100644 index 0000000..a6fb2cb --- /dev/null +++ b/test/samples/apache/invoker-02.xml @@ -0,0 +1,29 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="invoker"> + + <state id="invoker"> + <invoke type="scxml" src="invoked-02.xml"/> + <transition event="invoker.invoke.done" target="end" /> + </state> + + <state id="end" final="true" /> + +</scxml> diff --git a/test/samples/apache/invoker-03.xml b/test/samples/apache/invoker-03.xml new file mode 100644 index 0000000..be2d1fa --- /dev/null +++ b/test/samples/apache/invoker-03.xml @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used by InvokeTest#testInvoke03Sample() --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="invoker"> + + <state id="invoker"> + <onentry> + <log expr="'Invoker ...'"/> + </onentry> + <invoke type="scxml" src="invoked-03.xml"/> + <transition event="invoker.invoke.done" target="end"> + <log expr="' Outer invoke completed'"/> + </transition> + </state> + + <state id="end" final="true"> + <onentry> + <log expr="'Invoker completed'"/> + </onentry> + </state> + +</scxml> diff --git a/test/samples/apache/invoker-04.xml b/test/samples/apache/invoker-04.xml new file mode 100644 index 0000000..81c1bb8 --- /dev/null +++ b/test/samples/apache/invoker-04.xml @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- A fictitious state machine used by test cases. + Meant to illustrate the usage of SCXML <param> element as part + of an invocation using a custom invoker --> + +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="wait"> + + <datamodel> + <data id="foo"> + <bar>foo</bar> + </data> + </datamodel> + + <state id="wait"> + <transition event="test.trigger" target="first"/> + </state> + + <state id="first"> + <invoke src="FirstTestSrc" type="x-test"> + <param name="ding" expr="Data(foo,'node()')"/> + </invoke> + <transition event="test.trigger" target="second"/> + </state> + + <state id="second"> + <invoke src="SecondTestSrc" type="x-test"> + <param name="Data(foo,'node()')"/> + </invoke> + <transition event="test.trigger" target="third"/> + </state> + + <state id="third"> + <invoke src="ThirdTestSrc" type="x-test"> + <param name="Data(foo,'gibberish')"/> + </invoke> + </state> +</scxml>
\ No newline at end of file diff --git a/test/samples/apache/issue62-01-ext.xml b/test/samples/apache/issue62-01-ext.xml new file mode 100644 index 0000000..8c65a78 --- /dev/null +++ b/test/samples/apache/issue62-01-ext.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" + initialstate="ext"> + + <state id="ext"> + <transition event="foo"> + <log expr="'Stay transition in issue 62 test'"/> + </transition> + </state> + +</scxml> diff --git a/test/samples/apache/issue62-01.xml b/test/samples/apache/issue62-01.xml new file mode 100644 index 0000000..3599e5e --- /dev/null +++ b/test/samples/apache/issue62-01.xml @@ -0,0 +1,29 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" + initialstate="s1"> + + <state id="s1"> + <initial> + <transition target="s1.1" /> + </initial> + <state id="s1.1" src="issue62-01-ext.xml#ext" /> + <state id="s1.2" src="issue62-01-ext.xml#ext" /> + </state> + +</scxml> diff --git a/test/samples/apache/issue62-02-ext.xml b/test/samples/apache/issue62-02-ext.xml new file mode 100644 index 0000000..98da5d6 --- /dev/null +++ b/test/samples/apache/issue62-02-ext.xml @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" + initialstate="e1"> + + <state id="e1"> + <initial> + <transition target="e1.1"/> + </initial> + <state id="e1.1"> + <initial> + <transition target="e1.1.1"/> + </initial> + <state id="e1.1.1"> + <transition event="bar" target="e1.1.2"/> + </state> + <state id="e1.1.2"> + <transition event="baz" target="e1.2"/> + </state> + </state> + <state id="e1.2" final="true"/> + </state> + +</scxml> diff --git a/test/samples/apache/issue62-02.xml b/test/samples/apache/issue62-02.xml new file mode 100644 index 0000000..8e52e42 --- /dev/null +++ b/test/samples/apache/issue62-02.xml @@ -0,0 +1,35 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" + initialstate="s1"> + + <state id="s1"> + <transition event="foo" target="s2" /> + </state> + + <state id="s2"> + <initial> + <transition target="s2.1"/> + </initial> + <state id="s2.1" src="issue62-02-ext.xml#e1" /> + <transition event="s2.1.done" target="s3"/> + </state> + + <state id="s3" final="true"/> + +</scxml> diff --git a/test/samples/apache/issue62-03-ext.xml b/test/samples/apache/issue62-03-ext.xml new file mode 100644 index 0000000..0168447 --- /dev/null +++ b/test/samples/apache/issue62-03-ext.xml @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" + initialstate="e1"> + + <state id="e1"> + <initial> + <transition target="e1.1"/> + </initial> + <state id="e1.1"> + <initial> + <transition target="e1.1.1"/> + </initial> + <state id="e1.1.1"> + <transition event="bar" target="e1.1.2"/> + </state> + <state id="e1.1.2"> + <transition event="baz" target="e1.2"/> + </state> + </state> + <final id="e1.2"/> + </state> + +</scxml> diff --git a/test/samples/apache/issue62-03.xml b/test/samples/apache/issue62-03.xml new file mode 100644 index 0000000..46b8991 --- /dev/null +++ b/test/samples/apache/issue62-03.xml @@ -0,0 +1,35 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" + initialstate="s1"> + + <state id="s1"> + <transition event="foo" target="s2" /> + </state> + + <state id="s2"> + <initial> + <transition target="s2.1"/> + </initial> + <state id="s2.1" src="issue62-03-ext.xml#e1" /> + <transition event="s2.1.done" target="s3"/> + </state> + + <final id="s3"/> + +</scxml> diff --git a/test/samples/apache/issue64-01.xml b/test/samples/apache/issue64-01.xml new file mode 100644 index 0000000..bb848fe --- /dev/null +++ b/test/samples/apache/issue64-01.xml @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Correct SCXML document --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="tranbug"> + + <state id="tranbug"> + <datamodel> + <data id="dummy" expr="'somedata'"/> + </datamodel> + <onentry> + <log expr="'Begin transition bug test ...'" /> + </onentry> + <transition event="show.bug" target="end"> + <log expr="dummy" /> + <log expr="'*****' + dummy" /> + </transition> + </state> + <state id="end" final="true" /> + +</scxml> + + diff --git a/test/samples/apache/issue64-02.xml b/test/samples/apache/issue64-02.xml new file mode 100644 index 0000000..7e44d30 --- /dev/null +++ b/test/samples/apache/issue64-02.xml @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Bad SCXML document, many elements will be ignored with warnings from parser --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:my="http://my.foo.example/" + version="1.0" + initialstate="tranbug"> + + <datamodel> + <data id="foo"> + <alpha xmlns=""> + <beta/> + </alpha> + </data> + <misplaced/> + </datamodel> + + <state id="tranbug"> + <onentry> + <log expr="'Begin transition bug test ...'" /> + <foo/> + <my:bar/> + </onentry> + <transition event="show.bug" target="end"> + <!-- For example, FOLLOWING datamodel IS MISPLACED --> + <datamodel> + <data id="dummy" expr="'somedata'"/> + </datamodel> + <log expr="dummy" /> + <log expr="'*****' + dummy" /> + </transition> + </state> + + <my:baz/> + + <state id="end" final="true" /> + +</scxml> + + diff --git a/test/samples/apache/jsp-rootctx-test.xml b/test/samples/apache/jsp-rootctx-test.xml new file mode 100644 index 0000000..6a71579 --- /dev/null +++ b/test/samples/apache/jsp-rootctx-test.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initialstate="rootCtxTest"> + + <state id="rootCtxTest" final="true"> + <onentry> + <!-- 'foo' must exist in host JSP context --> + <assign name="foo" expr="${foo+1}" /> + <cs:var name="bar" expr="a brand new value" /> + </onentry> + </state> + +</scxml> diff --git a/test/samples/apache/log-on-config.xml b/test/samples/apache/log-on-config.xml new file mode 100644 index 0000000..b57780e --- /dev/null +++ b/test/samples/apache/log-on-config.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + $Id: log-on-config.xml 462838 2006-10-11 16:08:36Z rahul $ + +--> + +<!-- + + Dialog definitions for Shale Use Cases Example Web Application + written out as SCXML to demonstrate use of Commons SCXML as one + of Shale's Dialog Manager implementations. + + Related artifacts from <dialog name="Log On">...</dialog> + in original dialogs definition file from Shale nightlies. + +--> + +<scxml xmlns="http://www.w3.org/2005/01/SCXML" version="1.0" + initialstate="checkCookie"> + + + <state id="checkCookie"> + + <onentry> + <var name="cookieOutcome" + expr="#{profile$logon.check}" /> + </onentry> + + <transition cond="${cookieOutcome eq 'authenticated'}"> + <target next="exit"/> + </transition> + + <transition cond="${cookieOutcome eq 'unauthenticated'}"> + <target next="logon"/> + </transition> + + </state> + + <state id="logon"> + + <transition event="faces.outcome" + cond="${outcome eq 'authenticated'}"> + <target next="exit"/> + </transition> + + <transition event="faces.outcome" + cond="${outcome eq 'create'}"> + <target next="createProfile"/> + </transition> + + </state> + + <state id="createProfile" + src="edit-profile-config.xml" > + + <transition event="createProfile.done" + cond="${outcome eq 'success' or outcome eq 'cancel'}"> + <target next="exit"/> + </transition> + + </state> + + <state id="exit" + final="true" /> + +</scxml> diff --git a/test/samples/apache/microwave-01.xml b/test/samples/apache/microwave-01.xml new file mode 100644 index 0000000..86efa26 --- /dev/null +++ b/test/samples/apache/microwave-01.xml @@ -0,0 +1,78 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + This document uses JSP 2.0 EL as the expressions language. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initialstate="off"> + + <!-- trivial microwave oven example --> + <state id="off"> + <!-- off state --> + <transition event="turn_on" target="on"/> + </state> + + <state id="on"> + <initial> + <transition target="idle"/> + </initial> + + <!-- on/pause state --> + <onentry> + <!-- we assume the cook_time is passed in as a context parameter --> + <if cond="${empty cook_time}"> + <!-- default setting --> + <cs:var name="cook_time" expr="${5}"/> + </if> + <!-- again, door_closed should be a part of a global context --> + <if cond="${empty door_closed}"> + <!-- default setting --> + <cs:var name="door_closed" expr="${true}"/> + </if> + <!-- timer variable --> + <cs:var name="timer" expr="${0}"/> + </onentry> + + <transition event="turn_off" target="off"/> + + <transition cond="${timer ge cook_time}" target="off"/> + + <state id="idle"> + <!-- default immediate transition --> + <transition cond="${door_closed}" target="cooking"/> + + <!-- start cooking --> + <transition event="door_close" target="cooking"> + <assign name="door_closed" expr="${true}"/> + </transition> + </state> + + <state id="cooking"> + <transition event="door_open" target="idle"> + <assign name="door_closed" expr="${false}"/> + </transition> + <transition event="time" target="cooking"> + <assign name="timer" expr="${timer + 1}"/> + </transition> + </state> + + </state> + +</scxml> diff --git a/test/samples/apache/microwave-02-legacy.xml b/test/samples/apache/microwave-02-legacy.xml new file mode 100644 index 0000000..eaeb14a --- /dev/null +++ b/test/samples/apache/microwave-02-legacy.xml @@ -0,0 +1,87 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + This document uses JSP 2.0 EL as the expressions language. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" + initialstate="microwave"> + + <!-- trivial microwave oven example --> + <!-- using parallel and In() predicate --> + + <state id="microwave"> + <parallel id="parts"> + <state id="oven"> + <initial> + <transition target="off"/> + </initial> + + <state id="off"> + <!-- off state --> + <transition event="turn_on" target="on"/> + </state> + + <state id="on"> + <initial> + <transition target="idle"/> + </initial> + + <!-- on/pause state --> + <onentry> + <!-- we assume the cook_time is passed in as a context parameter --> + <if cond="${empty cook_time}"> + <!-- default setting --> + <var name="cook_time" expr="${5}"/> + </if> + <!-- timer variable --> + <var name="timer" expr="${0}"/> + </onentry> + + <transition event="turn_off" target="off"/> + + <transition cond="${timer ge cook_time}" target="off"/> + + <state id="idle"> + <transition cond="${In('closed')}" target="cooking"/> + </state> + + <state id="cooking"> + <transition cond="${not In('closed')}" target="idle"/> + + <transition event="time" target="cooking"> + <assign name="timer" expr="${timer + 1}"/> + </transition> + </state> + </state> + </state> + + <state id="door"> + <initial> + <transition target="closed"/> + </initial> + <state id="closed"> + <transition event="door_open" target="open"/> + </state> + <state id="open"> + <transition event="door_close" target="closed"/> + </state> + </state> + </parallel> + </state> + +</scxml> diff --git a/test/samples/apache/microwave-02.xml b/test/samples/apache/microwave-02.xml new file mode 100644 index 0000000..c03fe05 --- /dev/null +++ b/test/samples/apache/microwave-02.xml @@ -0,0 +1,87 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + This document uses JSP 2.0 EL as the expressions language. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initialstate="microwave"> + + <!-- trivial microwave oven example --> + <!-- using parallel and In() predicate --> + + <parallel id="microwave"> + <state id="oven"> + <initial> + <transition target="off"/> + </initial> + + <state id="off"> + <!-- off state --> + <transition event="turn_on" target="on"/> + </state> + + <state id="on"> + <initial> + <transition target="idle"/> + </initial> + + <!-- on/pause state --> + <onentry> + <!-- we assume the cook_time is passed in as a context parameter --> + <if cond="${empty cook_time}"> + <!-- default setting --> + <cs:var name="cook_time" expr="${5}"/> + </if> + <!-- timer variable --> + <cs:var name="timer" expr="${0}"/> + </onentry> + + <transition event="turn_off" target="off"/> + + <transition cond="${timer ge cook_time}" target="off"/> + + <state id="idle"> + <transition cond="${In('closed')}" target="cooking"/> + </state> + + <state id="cooking"> + <transition cond="${not In('closed')}" target="idle"/> + + <transition event="time" target="cooking"> + <assign name="timer" expr="${timer + 1}"/> + </transition> + </state> + </state> + </state> + + <state id="door"> + <initial> + <transition target="closed"/> + </initial> + <state id="closed"> + <transition event="door_open" target="open"/> + </state> + <state id="open"> + <transition event="door_close" target="closed"/> + </state> + </state> + </parallel> + +</scxml> diff --git a/test/samples/apache/microwave-03.xml b/test/samples/apache/microwave-03.xml new file mode 100644 index 0000000..887b1a6 --- /dev/null +++ b/test/samples/apache/microwave-03.xml @@ -0,0 +1,79 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + This document uses Commons JEXL as the expressions language. + Needs SCXMLParser. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initialstate="off"> + + <!-- trivial microwave oven example --> + <state id="off"> + <!-- off state --> + <transition event="turn_on" target="on"/> + </state> + + <state id="on"> + <initial> + <transition target="idle"/> + </initial> + + <!-- on/pause state --> + <onentry> + <!-- we assume the cook_time is passed in as a context parameter --> + <if cond="empty(cook_time)"> + <!-- default setting --> + <cs:var name="cook_time" expr="5"/> + </if> + <!-- again, door_closed should be a part of a global context --> + <if cond="empty(door_closed)"> + <!-- default setting --> + <cs:var name="door_closed" expr="true"/> + </if> + <!-- timer variable --> + <cs:var name="timer" expr="0"/> + </onentry> + + <transition event="turn_off" target="off"/> + + <transition cond="timer ge cook_time" target="off"/> + + <state id="idle"> + <!-- default immediate transition --> + <transition cond="door_closed" target="cooking"/> + + <!-- start cooking --> + <transition event="door_close" target="cooking"> + <assign name="door_closed" expr="true"/> + </transition> + </state> + + <state id="cooking"> + <transition event="door_open" target="idle"> + <assign name="door_closed" expr="false"/> + </transition> + <transition event="time" target="cooking"> + <assign name="timer" expr="timer + 1"/> + </transition> + </state> + + </state> + +</scxml> diff --git a/test/samples/apache/microwave-04.xml b/test/samples/apache/microwave-04.xml new file mode 100644 index 0000000..96656dd --- /dev/null +++ b/test/samples/apache/microwave-04.xml @@ -0,0 +1,90 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + This document uses Commons JEXL as the expressions language. + Needs SCXMLParser. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initialstate="microwave"> + + <!-- trivial microwave oven example --> + <!-- using parallel and In() predicate --> + + <parallel id="microwave"> + + <state id="oven"> + <initial> + <transition target="off"/> + </initial> + + <state id="off"> + <!-- off state --> + <transition event="turn_on" target="on"/> + </state> + + <state id="on"> + <initial> + <transition target="idle"/> + </initial> + + <!-- on/pause state --> + <onentry> + <!-- we assume the cook_time is passed in as a context parameter --> + <if cond="empty(cook_time)"> + <!-- default setting, note namespace of this custom action --> + <cs:var name="cook_time" expr="5"/> + </if> + <!-- timer variable --> + <cs:var name="timer" expr="0"/> + </onentry> + + <transition event="turn_off" target="off"/> + + <transition cond="timer ge cook_time" target="off"/> + + <state id="idle"> + <transition cond="In('closed')" target="cooking"/> + </state> + + <state id="cooking"> + <transition cond="not In('closed')" target="idle"/> + + <transition event="time" target="cooking"> + <assign name="timer" expr="timer + 1"/> + </transition> + </state> + </state> + </state> + + <state id="door"> + <initial> + <transition target="closed"/> + </initial> + <state id="closed"> + <transition event="door_open" target="open"/> + </state> + <state id="open"> + <transition event="door_close" target="closed"/> + </state> + </state> + + </parallel> + +</scxml> diff --git a/test/samples/apache/microwave-05.xml b/test/samples/apache/microwave-05.xml new file mode 100644 index 0000000..06b9fc9 --- /dev/null +++ b/test/samples/apache/microwave-05.xml @@ -0,0 +1,98 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + This document uses Commons JEXL as the expressions language. + Needs SCXMLParser. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initialstate="microwave"> + + <!-- trivial microwave oven example --> + <!-- using parallel (part of composite state) and In() predicate --> + + <state id="microwave"> + + <initial> + <transition target="parts"/> + </initial> + + <parallel id="parts"> + + <state id="oven"> + <initial> + <transition target="off"/> + </initial> + + <state id="off"> + <!-- off state --> + <transition event="turn_on" target="on"/> + </state> + + <state id="on"> + <initial> + <transition target="idle"/> + </initial> + + <!-- on/pause state --> + <onentry> + <!-- we assume the cook_time is passed in as a context parameter --> + <if cond="empty(cook_time)"> + <!-- default setting, note namespace of this custom action --> + <cs:var name="cook_time" expr="5"/> + </if> + <!-- timer variable --> + <cs:var name="timer" expr="0"/> + </onentry> + + <transition event="turn_off" target="off"/> + + <transition cond="timer ge cook_time" target="off"/> + + <state id="idle"> + <transition cond="In('closed')" target="cooking"/> + </state> + + <state id="cooking"> + <transition cond="not In('closed')" target="idle"/> + + <transition event="time" target="cooking"> + <assign name="timer" expr="timer + 1"/> + </transition> + </state> + </state> + </state> + + <state id="door"> + <initial> + <transition target="closed"/> + </initial> + <state id="closed"> + <transition event="door_open" target="open"/> + </state> + <state id="open"> + <transition event="door_close" target="closed"/> + </state> + </state> + + </parallel> + + </state> + +</scxml> diff --git a/test/samples/apache/parallel-01.xml b/test/samples/apache/parallel-01.xml new file mode 100644 index 0000000..e822285 --- /dev/null +++ b/test/samples/apache/parallel-01.xml @@ -0,0 +1,70 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + Needs SCXMLParser +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="para"> + + <parallel id="para"> + + <state id="para1"> + + <initial> + <transition target="para11"/> + </initial> + + <state id="para11"> + <transition event="foo" target="para12"/> + </state> + + <state id="para12"> + <onexit> + <log expr="'Exiting para12'" /> + </onexit> + </state> + + </state> + + <state id="para2"> + + <initial> + <transition target="para21"/> + </initial> + + <state id="para21"> + <transition cond="In('para12')" target="para22"/> + </state> + + <state id="para22"> + <onexit> + <log expr="'Exiting para22'" /> + </onexit> + + <transition target="end"/> + </state> + + </state> + + </parallel> + + <state id="end" final="true" /> + +</scxml> + diff --git a/test/samples/apache/parallel-02.xml b/test/samples/apache/parallel-02.xml new file mode 100644 index 0000000..106fc86 --- /dev/null +++ b/test/samples/apache/parallel-02.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + Needs SCXMLParser +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initialstate="state0"> + + <parallel id="state0"> + <transition event="event1" target="state1" /> + <!-- dummy regions --> + <state id="state01"/> + <state id="state02"/> + </parallel> + <final id="state1"/> + +</scxml> diff --git a/test/samples/apache/parallel-03.xml b/test/samples/apache/parallel-03.xml new file mode 100644 index 0000000..d57196a --- /dev/null +++ b/test/samples/apache/parallel-03.xml @@ -0,0 +1,118 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + Needs SCXMLParser +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initial="para"> + + <datamodel> + <data id="root"> + <root xmlns=""> + <count>0</count> + </root> + </data> + </datamodel> + + <parallel id="para"> + + <onentry> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onentry> + + <state id="para1"> + + <initial> + <transition target="para11"/> + </initial> + <onentry> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onentry> + + <state id="para11"> + <onentry> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onentry> + <transition event="foo" target="para12"/> + <onexit> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onexit> + </state> + + <final id="para12"> + <onentry> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onentry> + <onexit> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onexit> + </final> + + <onexit> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onexit> + + </state> + + <state id="para2"> + + <initial> + <transition target="para21"/> + </initial> + <onentry> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onentry> + + <state id="para21"> + <onentry> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onentry> + <transition event="bar" target="para22"/> + <onexit> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onexit> + </state> + + <final id="para22"> + <onentry> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onentry> + <onexit> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onexit> + </final> + + <onexit> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onexit> + + </state> + + <transition event="para.done" target="end"/> + + <onexit> + <assign location="Data(root, 'root/count')" expr="Data(root, 'root/count') + 1"/> + </onexit> + + </parallel> + + <state id="end" final="true" /> + +</scxml> + diff --git a/test/samples/apache/prefix-01.xml b/test/samples/apache/prefix-01.xml new file mode 100644 index 0000000..79cbe69 --- /dev/null +++ b/test/samples/apache/prefix-01.xml @@ -0,0 +1,29 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml:scxml xmlns:scxml="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="ten"> + + <scxml:state id="ten"> + <scxml:transition event="ten.done" target="twenty"/> + </scxml:state> + + <scxml:state id="twenty" final="true" /> + +</scxml:scxml> + diff --git a/test/samples/apache/scxml-initial-attr.xml b/test/samples/apache/scxml-initial-attr.xml new file mode 100644 index 0000000..0965653 --- /dev/null +++ b/test/samples/apache/scxml-initial-attr.xml @@ -0,0 +1,25 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for SrcTest.java in io package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initial="foo"> + + <final id="foo"/> + +</scxml> diff --git a/test/samples/apache/send-01.xml b/test/samples/apache/send-01.xml new file mode 100644 index 0000000..4f3cf40 --- /dev/null +++ b/test/samples/apache/send-01.xml @@ -0,0 +1,47 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="ten"> + + <state id="ten"> + <transition event="ten.done" target="twenty"> + <send sendid="send1" delay="'0'" + target="'http://localhost:8080/VXMLInterpreter'" type="'v3'" + xmlns:v3="http://foo.bar.com/vxml3" + xmlns:test="http://my.test.namespace"> + <v3:form id="Confirm"> + <v3:grammar type="application/srgs+xml" + src="/grammars/boolean.grxml"/> + <v3:block> + <v3:prompt>Say yes or no.</v3:prompt> + </v3:block> + </v3:form> + <test:foo id="foo1"> + <test:bar id="bar1" /> + </test:foo> + <test:foo id="foo2"> + <v3:prompt>This is just an example.</v3:prompt> + </test:foo> + </send> + </transition> + </state> + + <state id="twenty" final="true" /> + +</scxml> diff --git a/test/samples/apache/send-02.xml b/test/samples/apache/send-02.xml new file mode 100644 index 0000000..9685385 --- /dev/null +++ b/test/samples/apache/send-02.xml @@ -0,0 +1,89 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Various send usages that fire the events on the existing execution + engine. --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="ten"> + + <!-- We are expected to just fall all the way through down to + the state "seventy", then hop over and end up in "ninety" --> + <state id="ten"> + <onentry> + <send event="'ten.' + 'done'" /> + </onentry> + <transition event="ten.done" target="twenty" /> + </state> + + <state id="twenty"> + <onentry> + <send event="'twenty.done'" type="'scxml'" /> + </onentry> + <transition event="twenty.done" target="thirty" /> + </state> + + <state id="thirty"> + <onentry> + <send event="'thirty.done'" type="' sCxML '" /> + </onentry> + <transition event="thirty.done" target="forty" /> + </state> + + <state id="forty"> + <onentry> + <send event="'forty.done'" type=" " target=" " /> + </onentry> + <transition event="forty.done" target="fifty" /> + </state> + + <state id="fifty"> + <onentry> + <send event="'fifty.done'" target="' '" /> + </onentry> + <transition event="fifty.done" target="sixty" /> + </state> + + <state id="sixty"> + <onentry> + <send event="'sixty.done'" type="'scxml'" target=" " /> + </onentry> + <transition event="sixty.done" target="seventy" /> + </state> + + <state id="seventy"> + <onentry> + <send event="'seventy.done'" type="'scxml'" target="'foo'" /> + </onentry> + + <!-- This transition should not be followed since + target "foo" is unavailable (any target other + than an empty target is unavailable, empty target + is current execution i.e. this state machine) --> + <transition event="seventy.done" target="eighty" /> + + <!-- Since "foo" it not available, the event + "error.send.targetunavailable" should be raised --> + <transition event="error.send.targetunavailable" target="ninety" /> + + </state> + + <state id="eighty" final="true" /> + + <state id="ninety" final="true" /> + +</scxml> diff --git a/test/samples/apache/src-test-1.xml b/test/samples/apache/src-test-1.xml new file mode 100644 index 0000000..74e6ce2 --- /dev/null +++ b/test/samples/apache/src-test-1.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for SrcTest.java in io package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="srctest1"> + + <state id="srctest1" src="src-test-2.xml"> + <transition event="src.test" target="srctest3end"/> + <transition event="srctest1.done" target="srctest1end"/> + </state> + + <state id="srctest1end" final="true"/> + +</scxml> + diff --git a/test/samples/apache/src-test-2.xml b/test/samples/apache/src-test-2.xml new file mode 100644 index 0000000..16789ab --- /dev/null +++ b/test/samples/apache/src-test-2.xml @@ -0,0 +1,30 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for SrcTest.java in io package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="srctest2"> + + <state id="srctest2" src="src-test-3.xml"> + <transition event="srctest2.done" target="srctest2end"/> + </state> + + <state id="srctest2end" final="true"/> + +</scxml> + diff --git a/test/samples/apache/src-test-3.xml b/test/samples/apache/src-test-3.xml new file mode 100644 index 0000000..d675687 --- /dev/null +++ b/test/samples/apache/src-test-3.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for SrcTest.java in io package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="srctest3"> + + <state id="srctest3"/> + + <state id="srctest3end" final="true"/> + +</scxml> + diff --git a/test/samples/apache/src-test-4.xml b/test/samples/apache/src-test-4.xml new file mode 100644 index 0000000..4fe18e5 --- /dev/null +++ b/test/samples/apache/src-test-4.xml @@ -0,0 +1,30 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for SrcTest.java in io package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="srctest1"> + + <state id="srctest1" src="bad-document-404.xml"> + <transition event="srctest1.done" target="srctest1end"/> + </state> + + <state id="srctest1end" final="true"/> + +</scxml> + diff --git a/test/samples/apache/src-test-5.xml b/test/samples/apache/src-test-5.xml new file mode 100644 index 0000000..a36e112 --- /dev/null +++ b/test/samples/apache/src-test-5.xml @@ -0,0 +1,30 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Used for SrcTest.java in io package --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="srctest1"> + + <state id="srctest1" src="src-test-3.xml#badstateid404"> + <transition event="srctest1.done" target="srctest1end"/> + </state> + + <state id="srctest1end" final="true"/> + +</scxml> + diff --git a/test/samples/apache/state-01.xml b/test/samples/apache/state-01.xml new file mode 100644 index 0000000..22a9018 --- /dev/null +++ b/test/samples/apache/state-01.xml @@ -0,0 +1,24 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1"> + + <state id="s1" initial="s11"> + <final id="s11"/> + </state> + +</scxml> diff --git a/test/samples/apache/stateless-01.xml b/test/samples/apache/stateless-01.xml new file mode 100644 index 0000000..1229b8f --- /dev/null +++ b/test/samples/apache/stateless-01.xml @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="main"> + + <state id="main"> + + <initial> + <transition target="ten"/> + </initial> + + <onentry> + <var name="instancevar" expr="${20}" /> + </onentry> + + <state id="ten"> + <transition event="ten.done" cond="${instancevar eq 20}" + target="twenty" /> + <onexit> + <assign name="instancevar" expr="${30}" /> + </onexit> + </state> + + <state id="twenty"> + <transition event="twenty.done" cond="${instancevar eq 30}" + target="thirty" /> + </state> + + <state id="thirty" final="true" /> + + </state> + +</scxml> + diff --git a/test/samples/apache/stateless-parallel-01.xml b/test/samples/apache/stateless-parallel-01.xml new file mode 100644 index 0000000..f2e9017 --- /dev/null +++ b/test/samples/apache/stateless-parallel-01.xml @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="main"> + + <state id="main"> + + <parallel id="main.parallel"> + + <state id="state1"> + <initial> + <transition target="state1.init"/> + </initial> + <state id="state1.init"> + <transition event="state1.event" target="state1.final" /> + </state> + <state id="state1.final" final="true" /> + </state> + + <state id="state2"> + <initial> + <transition target="state2.init"/> + </initial> + <state id="state2.init"> + <transition event="state2.event" target="state2.final" /> + </state> + <state id="state2.final" final="true" /> + </state> + + </parallel> + + <transition event="main.parallel.done" target="next" /> + + </state> + + <state id="next" final="true"/> + +</scxml> diff --git a/test/samples/apache/static-method.xml b/test/samples/apache/static-method.xml new file mode 100644 index 0000000..7ad034a --- /dev/null +++ b/test/samples/apache/static-method.xml @@ -0,0 +1,32 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- Example of invoking a static method when using JEXL, for example: + (public static) java.lang.System#currentTimeMillis() +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="static"> + + <state id="static" final="true"> + <onentry> + <log expr="'Current time millis: ' + System.currentTimeMillis()" /> + </onentry> + </state> + +</scxml> + diff --git a/test/samples/apache/stopwatch.xml b/test/samples/apache/stopwatch.xml new file mode 100644 index 0000000..57572a1 --- /dev/null +++ b/test/samples/apache/stopwatch.xml @@ -0,0 +1,41 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="reset"> + + <state id="reset"> + <transition event="watch.start" target="running"/> + </state> + + <state id="running"> + <transition event="watch.split" target="paused"/> + <transition event="watch.stop" target="stopped"/> + </state> + + <state id="paused"> + <transition event="watch.unsplit" target="running"/> + <transition event="watch.stop" target="stopped"/> + </state> + + <state id="stopped"> + <transition event="watch.reset" target="reset"/> + </state> + +</scxml> + diff --git a/test/samples/apache/tie-breaker-01.xml b/test/samples/apache/tie-breaker-01.xml new file mode 100644 index 0000000..7cff894 --- /dev/null +++ b/test/samples/apache/tie-breaker-01.xml @@ -0,0 +1,35 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + Used by TieBreakerTest +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="ten"> + <state id="ten"> + <!-- twenty should win, document order --> + <transition event="ten.done" target="twenty" /> + <transition event="ten.done" target="thirty" /> + </state> + + <state id="twenty" final="true" /> + + <state id="thirty" final="true" /> + +</scxml> + diff --git a/test/samples/apache/tie-breaker-02.xml b/test/samples/apache/tie-breaker-02.xml new file mode 100644 index 0000000..62118dd --- /dev/null +++ b/test/samples/apache/tie-breaker-02.xml @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + Used by TieBreakerTest +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="ten"> + + <state id="ten"> + <initial> + <transition target="eleven"/> + </initial> + + <transition event="ten.done" target="twenty" /> + + <state id="eleven"> + <!-- thirty wins since eleven trumps + ten in the state heirarchy --> + <transition event="ten.done" target="thirty" /> + </state> + + </state> + + <state id="twenty" final="true" /> + + <state id="thirty" final="true" /> + +</scxml> + diff --git a/test/samples/apache/tie-breaker-03.xml b/test/samples/apache/tie-breaker-03.xml new file mode 100644 index 0000000..d850865 --- /dev/null +++ b/test/samples/apache/tie-breaker-03.xml @@ -0,0 +1,56 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + Used by TieBreakerTest +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="ten"> + + <state id="ten"> + <initial> + <transition target="eleven"/> + </initial> + + <transition event="ten.done" target="twenty" /> + + <transition event="ten.done" target="thirty" /> + + <state id="eleven"> + + <!-- forty wins due to document order and the fact + that since eleven trumps ten in the state + heirarchy --> + <transition event="ten.done" target="forty" /> + + <transition event="ten.done" target="fifty" /> + + </state> + + </state> + + <state id="twenty" final="true" /> + + <state id="thirty" final="true" /> + + <state id="forty" final="true" /> + + <state id="fifty" final="true" /> + +</scxml> + diff --git a/test/samples/apache/tie-breaker-04.xml b/test/samples/apache/tie-breaker-04.xml new file mode 100644 index 0000000..df87e63 --- /dev/null +++ b/test/samples/apache/tie-breaker-04.xml @@ -0,0 +1,36 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initialstate="s1"> + + <state id="s1"> + <initial> + <transition target="s11"/> + </initial> + + <transition event="event_1"/> + + <transition event="event_2" target="s1"/> + + <state id="s11"> + <transition event="event_1"/> + <transition event="event_2" target="s11"/> + </state> + </state> + + <state id="s2" final="true" /> +</scxml> diff --git a/test/samples/apache/tie-breaker-05.xml b/test/samples/apache/tie-breaker-05.xml new file mode 100644 index 0000000..1fcbd35 --- /dev/null +++ b/test/samples/apache/tie-breaker-05.xml @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initialstate="para"> + +<!-- + Testing dispute resolution in regions + {s11,s2111,s212} -event1-> {s12,s2112,s212} +--> + + <parallel id="para"> <!-- Outer parallel --> + + <state id="s1"> + <initial> + <transition target="s11"/> + </initial> + <state id="s11"> + <transition event="event1" target="s12"/> + <transition event="event1" target="s13"/> + </state> + <state id="s12"/> + <state id="s13"/> + </state> + + <state id="s2"> + <initial> + <transition target="para1"/> + </initial> + + <parallel id="para1"> <!-- Inner parallel --> + <state id="s211"> + <initial> + <transition target="s2111"/> + </initial> + <state id="s2111"> + <transition event="event1" target="s2112"/> + <transition event="event1" target="s2113"/> + </state> + <state id="s2112"/> + <state id="s2113"/> + </state> + <state id="s212"/> + </parallel> + + <state id="s22"/> + </state> + + </parallel> + +</scxml> diff --git a/test/samples/apache/tie-breaker-06.xml b/test/samples/apache/tie-breaker-06.xml new file mode 100644 index 0000000..a7f26ff --- /dev/null +++ b/test/samples/apache/tie-breaker-06.xml @@ -0,0 +1,40 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- A fictitious state machine used by test cases. + <send>s in the same executable content block --> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initialstate="main"> + + <state id="main"> + + <onentry> + <event name="internal_event1"/> + <event name="internal_event2"/> + </onentry> + + <transition event="internal_event1"> + <log label="Expected" + expr="'Transition for first event (internal_event1) followed'"/> + </transition> + <transition event="internal_event2"> + <log label="Unexpected" + expr="'Transition for second event (internal_event2) followed'"/> + </transition> + + </state> + +</scxml> diff --git a/test/samples/apache/transitions-01-legacy.xml b/test/samples/apache/transitions-01-legacy.xml new file mode 100644 index 0000000..f46bc74 --- /dev/null +++ b/test/samples/apache/transitions-01-legacy.xml @@ -0,0 +1,140 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="ten"> + + <!-- Start with a simple state --> + <state id="ten"> + <transition event="ten.done" target="twenty" /> + </state> + + <!-- Follow up with a composite state --> + <state id="twenty"> + + <initial> + <transition target="twenty_one"/> + </initial> + + <onentry> + <log expr="'In twenty'" /> + </onentry> + + <state id="twenty_one"> + <transition event="twenty_one.done" target="twenty_two"/> + </state> + + <state id="twenty_two"> + <transition event="twenty_two.done" target="thirty"/> + </state> + + </state> + + <!-- Finally, try an orthogonal state --> + <state id="thirty"> + + <parallel id="thirties_parallel"> + + <!-- The first (of three) regions in thirties_parallel --> + <state id="thirty_one"> + + <initial> + <transition target="thirty_one_child_one"/> + </initial> + <onentry> + <log expr="'Entering thirty_one'" /> + </onentry> + <transition event="thirty_one.done" target="forty"/> + + <state id="thirty_one_child_one"> + <onexit> + <log expr="'Exiting thirty_one_child_one'" /> + </onexit> + <transition event="thirty_one_child_one.done" + target="thirty_one_child_two"/> + </state> + + <state id="thirty_one_child_two"> + <onexit> + <log expr="'Exiting thirty_one_child_two'" /> + </onexit> + </state> + + </state> + + <!-- The second (of three) regions in thirties_parallel --> + <state id="thirty_two"> + + <initial> + <transition target="thirty_two_child_one"/> + </initial> + <onentry> + <log expr="'Entering thirty_two'" /> + </onentry> + + <state id="thirty_two_child_one"> + <onexit> + <log expr="'Exiting thirty_two_child_one'" /> + </onexit> + <transition event="thirty_two_child_one.done" + target="thirty_two_child_two"/> + </state> + + <state id="thirty_two_child_two"> + <onexit> + <log expr="'Exiting thirty_two_child_two'" /> + </onexit> + </state> + + </state> + + <!-- The third (of three) regions in thirties_parallel --> + <state id="thirty_three"> + + <initial> + <transition target="thirty_three_child_one"/> + </initial> + <onentry> + <log expr="'Entering thirty_three'" /> + </onentry> + + <state id="thirty_three_child_one"> + <onexit> + <log expr="'Exiting thirty_three_child_one'" /> + </onexit> + <transition event="thirty_three_child_one.done" + target="thirty_three_child_two"/> + </state> + + <state id="thirty_three_child_two"> + <onexit> + <log expr="'Exiting thirty_three_child_two'" /> + </onexit> + </state> + + </state> + + </parallel> + + </state> + + <!-- Declare victory --> + <state id="forty" final="true" /> + +</scxml> + diff --git a/test/samples/apache/transitions-01.xml b/test/samples/apache/transitions-01.xml new file mode 100644 index 0000000..ee5133f --- /dev/null +++ b/test/samples/apache/transitions-01.xml @@ -0,0 +1,136 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml + version="1.0" + initial="ten"> + + <!-- Start with a simple state --> + <state id="ten"> + <transition event="ten.done" target="twenty" /> + </state> + + <!-- Follow up with a composite state --> + <state id="twenty"> + + <initial> + <transition target="twenty_one"/> + </initial> + + <onentry> + <log expr="'In twenty'" /> + </onentry> + + <state id="twenty_one"> + <transition event="twenty_one.done" target="twenty_two"/> + </state> + + <state id="twenty_two"> + <transition event="twenty_two.done" target="thirty"/> + </state> + + </state> + + <!-- Finally, try an orthogonal state --> + <parallel id="thirty"> + + <!-- The first (of three) regions in thirties_parallel --> + <state id="thirty_one"> + + <initial> + <transition target="thirty_one_child_one"/> + </initial> + <onentry> + <log expr="'Entering thirty_one'" /> + </onentry> + <transition event="thirty_one.done" target="forty"/> + + <state id="thirty_one_child_one"> + <onexit> + <log expr="'Exiting thirty_one_child_one'" /> + </onexit> + <transition event="thirty_one_child_one.done" + target="thirty_one_child_two"/> + </state> + + <state id="thirty_one_child_two"> + <onexit> + <log expr="'Exiting thirty_one_child_two'" /> + </onexit> + </state> + + </state> + + <!-- The second (of three) regions in thirties_parallel --> + <state id="thirty_two"> + + <initial> + <transition target="thirty_two_child_one"/> + </initial> + <onentry> + <log expr="'Entering thirty_two'" /> + </onentry> + + <state id="thirty_two_child_one"> + <onexit> + <log expr="'Exiting thirty_two_child_one'" /> + </onexit> + <transition event="thirty_two_child_one.done" + target="thirty_two_child_two"/> + </state> + + <state id="thirty_two_child_two"> + <onexit> + <log expr="'Exiting thirty_two_child_two'" /> + </onexit> + </state> + + </state> + + <!-- The third (of three) regions in thirties_parallel --> + <state id="thirty_three"> + + <initial> + <transition target="thirty_three_child_one"/> + </initial> + <onentry> + <log expr="'Entering thirty_three'" /> + </onentry> + + <state id="thirty_three_child_one"> + <onexit> + <log expr="'Exiting thirty_three_child_one'" /> + </onexit> + <transition event="thirty_three_child_one.done" + target="thirty_three_child_two"/> + </state> + + <state id="thirty_three_child_two"> + <onexit> + <log expr="'Exiting thirty_three_child_two'" /> + </onexit> + </state> + + </state> + + </parallel> + + <!-- Declare victory --> + <state id="forty" final="true" /> + +</scxml> + diff --git a/test/samples/apache/transitions-02.xml b/test/samples/apache/transitions-02.xml new file mode 100644 index 0000000..7c1dcf5 --- /dev/null +++ b/test/samples/apache/transitions-02.xml @@ -0,0 +1,53 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initialstate="ten"> + + <state id="ten"> + + <onentry> + <cs:var name="foo" expr="1" /> + <log expr="'Foo is:' + foo" /> + </onentry> + + <!-- stay transition --> + <transition event="ten.stay"> + <assign name="foo" expr="foo + 1" /> + <log expr="'Foo is:' + foo" /> + </transition> + + <!-- self transition --> + <transition event="ten.self" target="ten"> + <assign name="foo" expr="foo + 1" /> + <log expr="'Foo is:' + foo" /> + </transition> + + <!-- "regular" transition --> + <transition event="ten.done" target="twenty"> + <assign name="foo" expr="foo + 1" /> + <log expr="'Foo is:' + foo" /> + </transition> + + </state> + + <state id="twenty" final="true" /> + +</scxml> + diff --git a/test/samples/apache/transitions-03.xml b/test/samples/apache/transitions-03.xml new file mode 100644 index 0000000..303d673 --- /dev/null +++ b/test/samples/apache/transitions-03.xml @@ -0,0 +1,88 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="ten"> + + <state id="ten"> + <transition event="ten.done" + target="twenty_one_2 twenty_two_2 twenty_three_2" /> + </state> + + <parallel id="twenty"> + + <state id="twenty_one"> + + <initial> + <transition target="twenty_one_1"/> + </initial> + + <state id="twenty_one_1"> + <transition event="foo" target="twenty_one_2"/> + </state> + + <state id="twenty_one_2"> + <onexit> + <log expr="'Exiting twenty_two_2'" /> + </onexit> + </state> + + </state> + + <state id="twenty_two"> + + <initial> + <transition target="twenty_two_1"/> + </initial> + + <state id="twenty_two_1"> + <transition event="foo" target="twenty_two_2"/> + </state> + + <state id="twenty_two_2"> + <onexit> + <log expr="'Exiting twenty_two_2'" /> + </onexit> + </state> + + </state> + + <state id="twenty_three"> + + <initial> + <transition target="twenty_three_1"/> + </initial> + + <state id="twenty_three_1"> + <transition event="foo" target="twenty_three_2"/> + </state> + + <state id="twenty_three_2"> + <onexit> + <log expr="'Exiting twenty_three_2'" /> + </onexit> + </state> + + </state> + + </parallel> + + <state id="thirty" final="true" /> + +</scxml> + diff --git a/test/samples/apache/transitions-04.xml b/test/samples/apache/transitions-04.xml new file mode 100644 index 0000000..547b702 --- /dev/null +++ b/test/samples/apache/transitions-04.xml @@ -0,0 +1,93 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<!-- + Needs SCXMLParser +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="ten"> + + <state id="ten"> + <transition event="ten.done" + target="twenty" /> + </state> + + <parallel id="twenty"> + + <state id="twenty_one"> + + <initial> + <transition target="twenty_one_1"/> + </initial> + + <transition event="bar" target="thirty"/> + + <state id="twenty_one_1"> + <transition event="foo" target="twenty_one_2"/> + </state> + + <state id="twenty_one_2"> + <onexit> + <log expr="'Exiting twenty_two_2'" /> + </onexit> + </state> + + </state> + + <state id="twenty_two"> + + <initial> + <transition target="twenty_two_1"/> + </initial> + + <state id="twenty_two_1"> + <transition event="foo" target="twenty_two_2"/> + </state> + + <state id="twenty_two_2"> + <onexit> + <log expr="'Exiting twenty_two_2'" /> + </onexit> + </state> + + </state> + + <state id="twenty_three"> + + <initial> + <transition target="twenty_three_1"/> + </initial> + + <state id="twenty_three_1"> + <transition event="foo" target="twenty_three_2"/> + </state> + + <state id="twenty_three_2"> + <onexit> + <log expr="'Exiting twenty_three_2'" /> + </onexit> + </state> + + </state> + + </parallel> + + <state id="thirty" final="true" /> + +</scxml> + diff --git a/test/samples/apache/transitions-05.xml b/test/samples/apache/transitions-05.xml new file mode 100644 index 0000000..7400359 --- /dev/null +++ b/test/samples/apache/transitions-05.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="init"> + <state id="init"> + <transition event="start" target="onetwo" /> + </state> + <parallel id="onetwo"> + <transition event="onetwo_three" target="three" /> + <state id="one"> + </state> + <state id="two"> + <transition event="two_four" target="four" /> + </state> + </parallel> + <state id="three"> + <transition event="three_one" target="one" /> + <transition event="three_four" target="four" /> + </state> + <state id="four"> + <transition event="four_onetwo" target="onetwo" /> + <transition event="four_three" target="three" /> + </state> +</scxml> diff --git a/test/samples/apache/travel-dialog.xml b/test/samples/apache/travel-dialog.xml new file mode 100644 index 0000000..08ebc99 --- /dev/null +++ b/test/samples/apache/travel-dialog.xml @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/01/SCXML" + version="1.0" + initialstate="tripType"> + + <state id="tripType"> + <transition event="tripType.done"> + <target next="departureCity"/> + </transition> + </state> + + <state id="departureCity"> + <transition event="departureCity.done"> + <target next="arrivalCity"/> + </transition> + </state> + + <state id="arrivalCity"> + <transition event="arrivalCity.done"> + <target next="departureDate"/> + </transition> + </state> + + <state id="departureDate"> + <transition event="departureDate.done" + cond="${tripType == 'round'}"> + <target next="arrivalDate"/> + </transition> + <transition event="departureDate.done" + cond="${tripType == '1way'}"> + <exit /> + </transition> + </state> + + <state id="arrivalDate" + final="true" /> + +</scxml> diff --git a/test/samples/apache/wildcard-01.xml b/test/samples/apache/wildcard-01.xml new file mode 100644 index 0000000..9f7ddc3 --- /dev/null +++ b/test/samples/apache/wildcard-01.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + xmlns:cs="http://commons.apache.org/scxml" + version="1.0" + initialstate="state1"> + <state id="state1"> + <onentry> + <cs:var name="switch" expr="4" /> + </onentry> + <!-- We'll trigger 'foo.bar.baz' event, + and therefore, must end up in state4 --> + <transition event="*" cond="switch eq 2" target="state2"/> + <transition event="foo.*" cond="switch eq 3" target="state3"/> + <transition event="foo.bar.*" cond="switch eq 4" target="state4"/> + </state> + <state id="state2" final="true"/> + <state id="state3" final="true"/> + <state id="state4" final="true"/> +</scxml> diff --git a/test/samples/apache/wildcard-02.xml b/test/samples/apache/wildcard-02.xml new file mode 100644 index 0000000..d039f69 --- /dev/null +++ b/test/samples/apache/wildcard-02.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="state1"> + + <state id="state1"> + <onentry> + <event name="success.start"/> + </onentry> + <transition event="success.*" target="state2"/> + </state> + + <state id="state2"/> + +</scxml> + diff --git a/test/samples/apache/wizard-01.xml b/test/samples/apache/wizard-01.xml new file mode 100644 index 0000000..87492a6 --- /dev/null +++ b/test/samples/apache/wizard-01.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="state1"> + <state id="state1"> + <transition event="event2" target="state2"/> + <transition event="event3" target="state3"/> + <transition event="event4" target="state4"/> + </state> + <state id="state2"> + <transition event="event1" target="state1"/> + <transition event="event3" target="state3"/> + <transition event="event4" target="state4"/> + </state> + <state id="state3"> + <transition event="event1" target="state1"/> + <transition event="event2" target="state2"/> + <transition event="event4" target="state4"/> + </state> + <state id="state4"> + <transition event="event1" target="state1"/> + <transition event="event2" target="state2"/> + <transition event="event3" target="state3"/> + </state> +</scxml> diff --git a/test/samples/apache/wizard-02.xml b/test/samples/apache/wizard-02.xml new file mode 100644 index 0000000..924b83f --- /dev/null +++ b/test/samples/apache/wizard-02.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initialstate="state1"> + <state id="state1"> + <onentry> + <!-- Send without a type causes the + default to be chosen as "scxml". + This will cause the first transition + to state2 to be immediately followed. --> + <send event="'event2'" /> + </onentry> + <transition event="event2" target="state2"/> + <transition event="event3" target="state3"/> + <transition event="event4" target="state4"/> + </state> + <state id="state2"> + <onentry> + <var name="aValue" expr="2"/> + <!-- Send with a non-empty (and not "scxml") + type causes the callback on the + EventDispatcher implementation. See + testWizard02Sample() in WizardsTest + (org.apache.commons.scxml test package) --> + <send namelist="aValue" type="'foo'" /> + </onentry> + <transition event="event1" target="state1"/> + <transition event="event3" target="state3"/> + <transition event="event4" target="state4"/> + </state> + <state id="state3"> + <onentry> + <var name="aValue" expr="3"/> + <send namelist="aValue" type="'foo'" /> + </onentry> + <transition event="event1" target="state1"/> + <transition event="event2" target="state2"/> + <transition event="event4" target="state4"/> + </state> + <state id="state4"> + <onentry> + <var name="aValue" expr="4"/> + <send namelist="aValue" type="'foo'" /> + </onentry> + <transition event="event1" target="state1"/> + <transition event="event2" target="state2"/> + <transition event="event3" target="state3"/> + </state> +</scxml>
\ No newline at end of file diff --git a/test/samples/w3c/Blackjack.scxml b/test/samples/w3c/Blackjack.scxml new file mode 100644 index 0000000..4f55e53 --- /dev/null +++ b/test/samples/w3c/Blackjack.scxml @@ -0,0 +1,99 @@ +<?xml version="1.0"?> +<?access-control allow="*"?> +<scxml version="1.0" datamodel="ecmascript" initial="master"> <state id="master"> + <initial id="init1"> + <transition target="_home"/> + </initial> + <transition event="new_dealer" target="NewDealer"/> + <transition event="mumble" target="_home"/> <!-- bail out to caller --> + <transition event="silence" target="_home"/> <!-- bail out to caller --> + <state id="_home"> + <onenter> + <script> + _data = {}; + </script> + </onenter> + <invoke src="datamodel.v3#InitDataModel" type="vxml3"> + <finalize> + <script> + var n; + for (n in event) { + _data[n] = event[n]; + } + </script> + </finalize> + </invoke> + <transition event="success" target="Welcome"/> + </state> + + <state id="Welcome"> + <invoke src="dialog.vxml#Welcome" type="vxml3"> + <param name="skinpath" expr="skinpath"/> + </invoke> + <transition event="success" target="Intro2"/> + </state> + + <state id="Intro2"> + <invoke src="dialog.vxml#Intro2" type="vxml3"> + <param name="skinpath" expr="skinpath"/> + </invoke> + <transition event="success" target="EvalDeal"/> + </state> + + <state id="EvalDeal"> + <onenter> + <script>enterEvalDeal();</script> + </onenter> + <invoke src="dialog.vxml#EvalDeal" type="vxml3"> + <param name="skinpath" expr="skinpath"/> + <param name="playercard1" expr="playercard1"/> + <param name="playercard2" expr="playercard2"/> + <param name="playertotal" expr="blackjack.GetTotalOf('caller').toString()"/> + <param name="dealercardshowing" expr="dealercardshowing"/> + </invoke> + <transition event="success" target="AskHit"/> + </state> + + <state id="AskHit"> + <invoke src="dialog.vxml#AskHit" type="vxml3"> + <param name="skinpath" expr="skinpath"/> + <finalize> + <script>finalizeAskHit();</script> + </finalize> + </invoke> + <transition event="hit" target="PlayNewCard"/> + <transition event="stand" target="PlayDone"/> + </state> + + <state id="PlayNewCard"> + <invoke src="dialog.vxml#PlayNewCard" type="vxml3"> + <param name="skinpath" expr="skinpath"/> + <param name="playernewcard" expr="playernewcard"/> + <param name="playertotal" expr="blackjack.GetTotalOf('caller').toString()"/> + </invoke> + <transition event="success" cond="blackjack.GetTotalOf('caller') >= 21" target="PlayDone"/> + <transition event="success" target="AskHit"/> <!-- less than 21 --> + </state> + + <state id="PlayDone"> + <onenter> + <script>enterPlayDone();</script> + </onenter> + <invoke src="dialog.vxml#PlayDone" type="vxml3"> + <param name="skinpath" expr="skinpath"/> + <param name="gameresult" expr="blackjack.GetGameResult()"/> + <param name="dealertotal" expr="blackjack.GetTotalOf('dealer').toString()"/> + </invoke> + <transition event="playagain" target="Intro2"/> + <transition event="quit" target="_home"/> + </state> + + <state id="NewDealer"> + <onenter> + <script>enterNewDealer();</script> + </onenter> + <invoke src="dialog.vxml#Dummy" type="vxml3"/> + <transition event="success" target="Welcome"/> + </state> + </state> +</scxml> diff --git a/test/samples/w3c/Main.scxml b/test/samples/w3c/Main.scxml new file mode 100644 index 0000000..4c03631 --- /dev/null +++ b/test/samples/w3c/Main.scxml @@ -0,0 +1,204 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- A wrapper state that contains all other states in this file +- it represents the complete state machine --> +<scxml + xmlns:xi="http://www.w3.org/2001/XInclude" + version="1.0" + initial="Main" + datamodel="ecmascript"> + <state id="Main"> + <!-- its initial state is Test1 --> + <initial> + <transition target="Test1"/> + </initial> + + <!-- Really simple state showing the basic syntax. --> + <state id="Test1"> + <initial> + <transition target="Test1Sub1"/> + </initial> + <!-- Runs before we go into the substate --> + <onentry> + <log expr="'Inside Test1'"/> + </onentry> + + <!-- Here is our first substate --> + <state id="Test1Sub1"> + <onentry> + <log expr="'Inside Test1Sub1.'"/> + </onentry> + <onexit> + <log expr="'Leaving Test1Sub1'"/> + </onexit> + <!-- Go to Sub2 on Event1 --> + <transition event="Event1" target="Test1Sub2"/> + </state> + + <!-- Here is the second substate + It is final, so Test1 is done when we get here --> + <final id="Test1Sub2"/> + + <!-- We get this event when we reach Test1Sub2. --> + <transition event="Test1.done" target="Test2"/> + + <!-- We run this on the way out of Test1 --> + <onexit> + <log expr="'Leaving Test1...'"/> + </onexit> + </state> + + <state id="Test2"> + <initial> + <transition target="Test2Sub1"/> + </initial> + + <!-- This time we reference a state + defined in an external file. --> + <xi:include href="Test2Sub1.xml" parse="text"/> + + <final id="Test2Sub2"/> + + <!-- Test2Sub2 is defined as final, so this + event is generated when we reach it --> + <transition event="done.state.Test2" next="Test3"/> + </state> + + <state id="Test3"> + <initial> + <transition target="Test3Sub1"/> + </initial> + + <state id="Test3Sub1"> + <onentry> + <log expr="'Inside Test3Sub1...'"/> + <!-- Send our self an event in 5s --> + <send event="'Timer'" delay="'5s'"/> + </onentry> + <!-- Transition on to Test4. + This will exit both us and our parent. --> + <transition event="Timer" target="Test4"/> + <onexit> + <log expr="'Leaving Test3Sub1...'"/> + </onexit> + </state> + + <onexit> + <log expr="'Leaving Test3...'"/> + </onexit> + </state> + + <state id="Test4"> + <onentry> + <log expr="'Inside Test4...'"/> + </onentry> + <initial> + <transition target="Test4Sub1"/> + </initial> + + <state id="Test4Sub1"> + <onexit> + <log expr="'Leaving Test4Sub1...'"/> + </onexit> + <!-- This transition causes the state to exit immediately + after entering Test4Sub1. The transition has no event + or guard so it is always active --> + <transition target="Test5"/> + </state> + </state> + + <state id="Test5"> + <onentry> + <log expr="'Inside Test5...'"/> + </onentry> + <initial> + <transition target="Test5P"/> + </initial> + + <!-- Fire off parallel states. In a more realistic example + the parallel substates Test5PSub1 and Test5PSub2 would themselves + have substates and would do some real work before transitioning to final substates --> + <parallel id="Test5P"> + <state id="Test5PSub1" initial="Test5PSub1Final"> + <final id="Test5PSub1Final"/> + </state> + <state id="Test5PSub2" initial="Test5PSub2Final"> + <final id="Test5PSub2Final"/> + </state> + <onexit> + <log expr="'all parallel states done'"/> + </onexit> + </parallel> + + <!-- The parallel states immediately transition to final substates, + so this event is generated immediately. --> + <transition event="done.state.Test5P" target="Test6"/> + </state> + + <!-- + - This state shows invocation of an external component. + - We will use CCXML + VoiceXML actions as an example + - as it is a good smoke test to show how it all + - fits together. + - Note: In a real app you would likely + - split this over several states but we + - are trying to keep it simple here. + --> + <state id="Test6" + xmlns:ccxml="http://www.w3.org/2002/09/ccxml" + xmlns:v3="http://www.w3.org/2005/07/vxml3"> + <datamodel> + <data name="ccxmlid" expr="32459"/> + <date name="v3id" expr="17620"/> + <data name="dest" expr="'tel:+18315552020'"/> + <data name="src" expr="'helloworld2.vxml'"/> + <data name="id" expr="'HelloWorld'"/> + </datamodel> + + <onentry> + <!-- Use <send> a message to a CCXML Processor asking it to run createcall --> + <send target="ccxmlid" type="basichttp" event="ccxml:createcall" namelist="dest"/> + </onentry> + + <transition event="ccxml:connection.connected"> + <!-- Here as a platform-specific extension we use example V3 + Custom Action Elements instead of send. The implementation of this logic + would be platform-dependent. --> + <v3:form id="HelloWorld"> + <v3:block><v3:prompt>Hello World!</v3:prompt></v3:block> + </v3:form> + </transition> + + <transition event="v3:HelloWorld.done"> + <!-- Here we are using the low level <send> + element to run a v3 form. Note that the event "v3:HelloWorld.done" + is assumed either to be set/sent explicitly by the v3:form code or + implicitly by some process outside of the v3:form --> + <send target="v3id" type="basichttp" event="v3:formstart" namelist="src id"/> + </transition> + + <transition event="v3:HelloWorld2.done"> + <!-- we use _event.data to access data in the event we're processing. + Again we assume the v3:HelloWorld2.done is set/sent from outside + this document --> + <ccxml:disconnect connectionid="_event.data.connectionid"/> + </transition> + + <transition event="ccxml:connection.disconnected" target="Done"/> + + <transition event="send.failed" target="Done"> + <!-- If we get an error event we move to the Done state that + is a final state. --> + <log expr="'Sending to and External component failed'"/> + </transition> + + <onexit> + <log expr="'Finished with external component'"/> + </onexit> + </state> + + <!-- This final state is an immediate child of Main + - when we get here, Main.done is generated. --> + <final id="Done"/> + <!-- End of Main > --> + </state> +</scxml>
\ No newline at end of file diff --git a/test/samples/w3c/Test2Sub1.xml b/test/samples/w3c/Test2Sub1.xml new file mode 100644 index 0000000..6ab7b98 --- /dev/null +++ b/test/samples/w3c/Test2Sub1.xml @@ -0,0 +1,9 @@ +<!-- This is an example substate defined in +- an external file and included by Main.scxml. +--> +<state id="Test2Sub1"> +<onentry> + <log expr="'Inside Test2Sub1'"/> +</onentry> +<transition event="Event2" target="Test2Sub2"/> +</state> diff --git a/test/samples/w3c/TrafficReport.scxml b/test/samples/w3c/TrafficReport.scxml new file mode 100644 index 0000000..09e2e93 --- /dev/null +++ b/test/samples/w3c/TrafficReport.scxml @@ -0,0 +1,88 @@ +<?xml version="1.0"?> +<?access-control allow="*"?> +<!-- A comment! --> +<scxml version="1.0" initial="Intro" datamodel="ecmascript"> + <state id="Intro"> + <invoke src="dialog.vxml#Intro" type="vxml2"/> + <transition event="success" cond="sessionChrome.playAds" target="PlayAds"/> + <transition event="success" cond="!sessionChrome.playAds && ANIQuality" + target="ShouldGoBack"/> + <transition event="success" cond="!sessionChrome.playAds && !ANIQuality" + target="StartOver"/> + </state> + + <state id="PlayAds"> + <invoke src="dialog.vxml#PlayAds" type="vxml2"/> + <transition event="success" cond="ANIQuality" target="ShouldGoBack"/> + <transition event="success" cond="!ANIQuality" target="StartOver"/> + </state> + + <state id="StartOver"> + <onentry> + <script>enterStartOver();</script> + </onentry> + <invoke src="dialog.vxml#StartOver" type="vxml2"> + <param name="gotItFromANI" expr="gotItFromANI"/> + <finalize> + <script>finalizeStartOver();</script> + </finalize> + </invoke> + <transition event="success" target="ShouldGoBack"/> + <transition event="doOver" target="StartOver"/> + <transition event="restart" target="Intro"/> <!-- bail out to caller --> + </state> + + <state id="ShouldGoBack"> + <invoke src="dialog.vxml#ShouldGoBack" type="vxml2"> + <param name="cityState" expr="cityState"/> + <param name="gotItFromANI" expr="gotItFromANI"/> + <finalize> + <script>finalizeShouldGoBack();</script> + </finalize> + </invoke> + <transition event="highWay" target="HighwayReport"/> + <transition event="go_back" target="StartOver"/> + <transition event="doOver" target="ShouldGoBack"/> + <transition event="restart" target="Intro"/> + </state> + + <state id="HighwayReport"> + <invoke src="dialog.vxml#HighwayReport" type="vxml2"> + <param name="cityState" expr="cityState"/> + <param name="gotItFromANI" expr="gotItFromANI"/> + <param name="playHRPrompt" expr="playHRPrompt"/> + <param name="metroArea" expr="metroArea"/> + <finalize> + <script>finalizeHighwayReport();</script> + </finalize> + </invoke> + <transition event="highway" target="PlayHighway"/> + <transition event="go_back" target="StartOver"/> + <transition event="doOver" target="HighwayReport"/> + <transition event="fullreport" target="FullReport"/> + <transition event="restart" target="Intro"/> + </state> + + <state id="FullReport"> + <invoke src="dialog.vxml#FullReport" type="vxml2"> + <param name="cityState" expr="cityState"/> + <param name="metroArea" expr="metroArea"/> + <finalize> + <script>finalizeFullReport();</script> + </finalize> + </invoke> + <transition event="go_back" target="HighwayReport"/> + <transition event="new_city" target="StartOver"/> + </state> + + <state id="PlayHighway"> + <invoke src="dialog.vxml#PlayHighway" type="vxml2"> + <param name="cityState" expr="cityState"/> + <param name="curHighway" expr="curHighway"/> + <finalize> + <script>finalizePlayHighway();</script> + </finalize> + </invoke> + <transition event="go_back" target="HighwayReport"/> + </state> +</scxml> diff --git a/test/samples/w3c/calc.scxml b/test/samples/w3c/calc.scxml new file mode 100644 index 0000000..e759b45 --- /dev/null +++ b/test/samples/w3c/calc.scxml @@ -0,0 +1,158 @@ +<?xml version="1.0" ?> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" + initial="on" datamodel="ecmascript" name="calc"> + <datamodel> + <data id="long_expr" /> + <data id="short_expr" expr="0" /> + <data id="res" /> + </datamodel> + <state id="wrapper" initial="on"> + <state id="on" initial="ready"> + <onentry> + <send event="DISPLAY.UPDATE" /> + </onentry> + <state id="ready" initial="begin"> + <state id="begin"> + <transition event="OPER.MINUS" target="negated1" /> + <onentry> + <send event="DISPLAY.UPDATE" /> + </onentry> + </state> + <state id="result"> + </state> + <transition event="OPER" target="opEntered" /> + <transition event="DIGIT.0" target="zero1"> + <assign location="short_expr" expr="''" /> + </transition> + <transition event="DIGIT" target="int1"> + <assign location="short_expr" expr="''" /> + </transition> + <transition event="POINT" target="frac1"> + <assign location="short_expr" expr="''" /> + </transition> + </state> + <state id="negated1"> + <onentry> + <assign location="short_expr" expr="'-'" /> + <send event="DISPLAY.UPDATE" /> + </onentry> + <transition event="DIGIT.0" target="zero1" /> + <transition event="DIGIT" target="int1" /> + <transition event="POINT" target="frac1" /> + </state> + <state id="operand1"> + <state id="zero1"> + <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int1" /> + <transition event="POINT" target="frac1" /> + </state> + <state id="int1"> + <transition event="POINT" target="frac1" /> + <transition event="DIGIT"> + <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" /> + <send event="DISPLAY.UPDATE" /> + </transition> + <onentry> + <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" /> + <send event="DISPLAY.UPDATE" /> + </onentry> + </state> + <state id="frac1"> + <onentry> + <assign location="short_expr" expr="short_expr+'.'" /> + <send event="DISPLAY.UPDATE" /> + </onentry> + <transition event="DIGIT"> + <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" /> + <send event="DISPLAY.UPDATE" /> + </transition> + </state> + <transition event="OPER" target="opEntered" /> + </state> + <state id="opEntered"> + <transition event="OPER.MINUS" target="negated2" /> + <transition event="POINT" target="frac2" /> + <transition event="DIGIT.0" target="zero2" /> + <transition event="DIGIT" target="int2" /> + <onentry> + <raise event="CALC.SUB" /> + <send target="_internal" event="OP.INSERT"> + <param name="operator" expr="_event.name" /> + </send> + </onentry> + </state> + <state id="negated2"> + <onentry> + <assign location="short_expr" expr="'-'" /> + <send event="DISPLAY.UPDATE" /> + </onentry> + <transition event="DIGIT.0" target="zero2" /> + <transition event="DIGIT" target="int2" /> + <transition event="POINT" target="frac2" /> + </state> + <state id="operand2"> + <state id="zero2"> + <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int2" /> + <transition event="POINT" target="frac2" /> + </state> + <state id="int2"> + <transition event="DIGIT"> + <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" /> + <send event="DISPLAY.UPDATE" /> + </transition> + <onentry> + <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" /> + <send event="DISPLAY.UPDATE" /> + </onentry> + <transition event="POINT" target="frac2" /> + </state> + <state id="frac2"> + <onentry> + <assign location="short_expr" expr="short_expr +'.'" /> + <send event="DISPLAY.UPDATE" /> + </onentry> + <transition event="DIGIT"> + <assign location="short_expr" expr="short_expr +_event.name.substr(_event.name.lastIndexOf('.')+1)" /> + <send event="DISPLAY.UPDATE" /> + </transition> + </state> + <transition event="OPER" target="opEntered"> + <raise event="CALC.SUB" /> + <raise event="OP.INSERT" /> + </transition> + <transition event="EQUALS" target="result"> + <raise event="CALC.SUB" /> + <raise event="CALC.DO" /> + </transition> + </state> + <transition event="C" target="on" /> + </state> + <transition event="CALC.DO"> + <assign location="short_expr" expr="''+ res" /> + <assign location="long_expr" expr="''" /> + <assign location="res" expr="0" /> + </transition> + <transition event="CALC.SUB"> + <if cond="short_expr!=''"> + <assign location="long_expr" expr="long_expr+'('+short_expr+')'" /> + </if> + <assign location="res" expr="eval(long_expr)" /> + <assign location="short_expr" expr="''" /> + <send event="DISPLAY.UPDATE" /> + </transition> + <transition event="DISPLAY.UPDATE"> + <log level="0" label="'result'" expr=".short_expr==''?res:short_expr" /> + </transition> + <transition event="OP.INSERT"> + <log level="0" expr="_event.data[0]" /> + <if cond="_event.data[0] == 'OPER.PLUS'"> + <assign location="long_expr" expr="long_expr+'+'" /> + <elseif cond="_event.data[0]=='OPER.MINUS'" /> + <assign location="long_expr" expr="long_expr+'-'" /> + <elseif cond="_event.data[0]=='OPER.STAR'" /> + <assign location="long_expr" expr="long_expr+'*'" /> + <elseif cond="_event.data[0]=='OPER.DIV'" /> + <assign location="long_expr" expr="long_expr+'/'" /> + </if> + </transition> + </state> +</scxml> diff --git a/test/samples/w3c/edit-profile-config.scxml b/test/samples/w3c/edit-profile-config.scxml new file mode 100644 index 0000000..2f8753e --- /dev/null +++ b/test/samples/w3c/edit-profile-config.scxml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Dialog definitions for Shale Use Cases Example Web Application + written out as SCXML to demonstrate use of Commons SCXML as one + of Shale's Dialog Manager implementations. + For details, see: http://shale.apache.org/shale-dialog-scxml/ +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" xmlns:my="http://scxml.example.com/" + version="1.0" initial="edit" datamodel="el"> + + <state id="edit"> + <initial> + <transition target="setup"/> + </initial> + + <!-- global transitions (within state "edit") --> + <transition event="faces.outcome" cond="${outcome eq 'cancel'}" target="cancel"/> + <transition event="faces.outcome" cond="${outcome eq 'finish'}" target="finish"/> + + <state id="setup"> + <onentry> + <my:var name="setupOutcome" expr="#{profile$edit.setup}" /> + </onentry> + <transition cond="${setupOutcome eq 'success'}" target="page1"/> + </state> + + <state id="page1"> + <transition event="faces.outcome" cond="${outcome eq 'next'}" target="page2"/> + </state> + + <state id="page2"> + + <transition event="faces.outcome" cond="${outcome eq 'previous'}" target="page1"/> + <transition event="faces.outcome" cond="${outcome eq 'next'}" target="page3"/> + + </state> + + <state id="page3"> + <transition event="faces.outcome" cond="${outcome eq 'previous'}" target="page2"/> + <transition event="faces.outcome" cond="${outcome eq 'next'}" target="editExit"/> + </state> + + </state> + + <state id="cancel"> + + <onentry> + <my:var name="cancelOutcome" expr="#{profile$edit.cancel}" /> + </onentry> + <transition cond="${cancelOutcome eq 'success'}" target="editExit"> + <my:var name="outcome" expr="cancel"/> + </transition> + </state> + + <state id="finish"> + + <onentry> + <my:var name="finishOutcome" expr="#{profile$edit.finish}" /> + </onentry> + + <transition cond="${finishOutcome eq 'username'}" target="page1"/> + <transition cond="${finishOutcome eq 'password'}" target="page1"/> + <transition cond="${finishOutcome eq 'success'}" target="editExit"> + <my:var name="outcome" expr="success"/> + </transition> + </state> + + <final id="editExit"/> + +</scxml> diff --git a/test/samples/w3c/log-on-config.scxml b/test/samples/w3c/log-on-config.scxml new file mode 100644 index 0000000..01f45d6 --- /dev/null +++ b/test/samples/w3c/log-on-config.scxml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Dialog definitions for Shale Use Cases Example Web Application + written out as SCXML to demonstrate use of Commons SCXML as one + of Shale's Dialog Manager implementations. + For details, see: http://shale.apache.org/shale-dialog-scxml/ +--> +<scxml xmlns="http://www.w3.org/2005/07/scxml" xmlns:my="http://scxml.example.com/" + version="1.0" initial="checkCookie" datamodel="el" > + + <state id="checkCookie"> + <onentry> + <my:var name="cookieOutcome" expr="#{profile$logon.check}" /> + </onentry> + <transition cond="${cookieOutcome eq 'authenticated'}" target="exit"/> + <transition cond="${cookieOutcome eq 'unauthenticated'}" target="logon"/> + + </state> + + <state id="logon"> + <transition event="faces.outcome" cond="${outcome eq 'authenticated'}" target="exit"/> + <transition event="faces.outcome" cond="${outcome eq 'create'}" target="createProfile"/> + </state> + + + <state id="createProfile" src="edit-profile-config.xml" > + <transition event="createProfile.done" cond="${outcome eq 'success' or outcome eq 'cancel'}" target="exit"/> + </state> + + <final id="exit"/> + +</scxml> diff --git a/test/samples/w3c/microwave-01.scxml b/test/samples/w3c/microwave-01.scxml new file mode 100644 index 0000000..71e2f98 --- /dev/null +++ b/test/samples/w3c/microwave-01.scxml @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + datamodel="ecmascript" + initial="off"> + + <!-- trivial 5 second microwave oven example --> + <datamodel> + <data id="cook_time" expr="5"/> + <data id="door_closed" expr="true"/> + <data id="timer" expr="0"/> + </datamodel> + + <state id="off"> + <!-- off state --> + <transition event="turn.on" target="on"/> + </state> + + <state id="on"> + <initial> + <transition target="idle"/> + </initial> + <!-- on/pause state --> + + <transition event="turn.off" target="off"/> + <transition cond="timer >= cook_time" target="off"/> + + <state id="idle"> + <!-- default immediate transition if door is shut --> + <transition cond="door_closed" target="cooking"/> + <transition event="door.close" target="cooking"> + <assign location="door_closed" expr="true"/> + <!-- start cooking --> + </transition> + </state> + + <state id="cooking"> + <transition event="door.open" target="idle"> + <assign location="door_closed" expr="false"/> + </transition> + + <!-- a 'time' event is seen once a second --> + <transition event="time"> + <assign location="timer" expr="timer + 1"/> + </transition> + </state> + + </state> + +</scxml> diff --git a/test/samples/w3c/microwave-02.scxml b/test/samples/w3c/microwave-02.scxml new file mode 100644 index 0000000..a96f1fd --- /dev/null +++ b/test/samples/w3c/microwave-02.scxml @@ -0,0 +1,63 @@ +<?xml version="1.0"?> +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + datamodel="ecmascript" + initial="oven"> + + <!-- trivial 5 second microwave oven example --> + <!-- using parallel and In() predicate --> + <datamodel> + <data id="cook_time" expr="5"/> + <data id="door_closed" expr="true"/> + <data id="timer" expr="0"/> + </datamodel> + + <parallel id="oven"> + + <!-- this region tracks the microwave state and timer --> + <state id="engine"> + <transition target="off"/> + + <state id="off"> + <!-- off state --> + <transition event="turn.on" target="on"/> + </state> + + <state id="on"> + <transition target="idle"/> + <!-- on/pause state --> + + <transition event="turn.off" target="off"/> + <transition cond="timer >= cook_time" target="off"/> + + <state id="idle"> + <transition cond="In('closed')" target="cooking"/> + </state> + + <state id="cooking"> + <transition cond="In('open')" target="idle"/> + + <!-- a 'time' event is seen once a second --> + <transition event="time"> + <assign location="timer" expr="timer + 1"/> + </transition> + </state> + </state> + </state> + + <!-- this region tracks the microwave door state --> + <state id="door"> + <initial> + <transition target="closed"/> + </initial> + <state id="closed"> + <transition event="door.open" target="open"/> + </state> + <state id="open"> + <transition event="door.close" target="closed"/> + </state> + </state> + + </parallel> + +</scxml> diff --git a/test/samples/w3c/simple.xml b/test/samples/w3c/simple.xml new file mode 100644 index 0000000..d3badc8 --- /dev/null +++ b/test/samples/w3c/simple.xml @@ -0,0 +1,9 @@ +<scxml xmlns="http://www.w3.org/2005/07/scxml" + version="1.0" + initial="Main" + datamodel="ecmascript"> + <state /> + <state /> + <state /> + <state /> +</scxml>
\ No newline at end of file diff --git a/test/schema/scxml-message.xsd b/test/schema/scxml-message.xsd new file mode 100644 index 0000000..de4b4b8 --- /dev/null +++ b/test/schema/scxml-message.xsd @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + XML Schema for sending messages to SCXML processors. +--> +<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" + targetNamespace="http://www.w3.org/2005/07/scxml" + xmlns="http://www.w3.org/2005/07/scxml" + elementFormDefault="qualified"> + + <xsd:annotation> + <xsd:documentation xml:lang="en"> + XML Schema for sending messages to SCXML processors. + Version 1.0 + </xsd:documentation> + <xsd:documentation source="scxml-copyright.xsd" /> + </xsd:annotation> + + <xsd:attributeGroup name="scxmlmessage.extra.attribs"> + <xsd:annotation> + <xsd:documentation> + Group allowing attributes from other namespaces + </xsd:documentation> + </xsd:annotation> + <xsd:anyAttribute namespace="##other" processContents="lax" /> + </xsd:attributeGroup> + + <xsd:attributeGroup name="scxmlmessage.message.attlist"> + <xsd:attribute name="version" type="xsd:string" fixed="1.0" use="required" /> + <xsd:attribute name="source" type="xsd:anyURI" use="required" /> + <xsd:attribute name="target" type="xsd:anyURI" use="required" /> + <xsd:attribute name="sendid" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation> + Non SCXML senders are not required to specify a sendid + </xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="name" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation> + Defaults to "external.event" + </xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="sourcetype" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation> + Defaults to "scxml" + </xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attributeGroup ref="scxmlmessage.extra.attribs" /> + </xsd:attributeGroup> + + <xsd:group name="scxmlmessage.message.content"> + <xsd:sequence> + <xsd:element ref="payload" minOccurs="1" maxOccurs="1" /> + </xsd:sequence> + </xsd:group> + + <xsd:complexType name="scxmlmessage.message.type"> + <xsd:group ref="scxmlmessage.message.content" /> + <xsd:attributeGroup ref="scxmlmessage.message.attlist" /> + </xsd:complexType> + + <xsd:element name="message" type="scxmlmessage.message.type" /> + + <xsd:attributeGroup name="scxmlmessage.payload.attlist"> + <xsd:attributeGroup ref="scxmlmessage.extra.attribs" /> + <xsd:attribute name="contenttype" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation> + The mime type of the child content. + </xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:attributeGroup> + + <xsd:group name="scxmlmessage.payload.content"> + <xsd:choice> + <xsd:sequence> + <xsd:element ref="property" minOccurs="0" + maxOccurs="unbounded" /> + </xsd:sequence> + <xsd:sequence> + <xsd:any namespace="##other" minOccurs="1" + maxOccurs="unbounded" processContents="lax" /> + </xsd:sequence> + </xsd:choice> + </xsd:group> + + <xsd:complexType name="scxmlmessage.payload.type"> + <xsd:group ref="scxmlmessage.payload.content" /> + <xsd:attributeGroup ref="scxmlmessage.payload.attlist" /> + </xsd:complexType> + + <xsd:element name="payload" type="scxmlmessage.payload.type" /> + + <xsd:attributeGroup name="scxmlmessage.property.attlist"> + <xsd:attribute name="name" type="xsd:string" use="required" /> + <xsd:attributeGroup ref="scxmlmessage.extra.attribs" /> + </xsd:attributeGroup> + + <xsd:group name="scxmlmessage.property.content"> + <xsd:sequence> + <xsd:element ref="hint" minOccurs="0" + maxOccurs="1" /> + <xsd:any namespace="##other" minOccurs="0" + maxOccurs="unbounded" processContents="skip" /> + </xsd:sequence> + </xsd:group> + + <xsd:complexType name="scxmlmessage.property.type" mixed="true"> + <xsd:group ref="scxmlmessage.property.content" /> + <xsd:attributeGroup ref="scxmlmessage.property.attlist" /> + </xsd:complexType> + + <xsd:element name="property" type="scxmlmessage.property.type" /> + + <xsd:element name="hint" type="xsd:string" /> + +</xsd:schema> diff --git a/test/schema/scxml.xsd b/test/schema/scxml.xsd new file mode 100644 index 0000000..ebc0654 --- /dev/null +++ b/test/schema/scxml.xsd @@ -0,0 +1,176 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" + targetNamespace="http://www.w3.org/2005/07/scxml" + xmlns="http://www.w3.org/2005/07/scxml" + elementFormDefault="qualified"> + + <xsd:annotation> + <xsd:documentation> + This is the XML Schema driver for SCXML 1.0. + Please use this namespace for SCXML 1.0 elements: + + "http://www.w3.org/2005/07/scxml" + + </xsd:documentation> + <xsd:documentation source="scxml-copyright.xsd"/> + </xsd:annotation> + <xsd:annotation> + <xsd:documentation> + This is the XML Schema driver file for SCXML 1.0. + + This schema: + + sets the namespace for SCXML 1.0 + + imports external schemas (xml.xsd) + + imports SCXML common datatypes, attributes and content models + + imports modular schemas + + SCXML 1.0 includes: + + SCXML core constructs + + SCXML executable content + + SCXML data model and manipulation + + SCXML external communications + + This schema is permissive such that it accomodates all + datamodels, but validating documents may contain markup that + is ignored in certain datamodels. + </xsd:documentation> + </xsd:annotation> + + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"> + <xsd:annotation> + <xsd:documentation> + This import brings in the XML namespace attributes + The XML attributes are used by various modules. + </xsd:documentation> + </xsd:annotation> + </xsd:import> + + <xsd:include schemaLocation="scxml-attribs.xsd"> + <xsd:annotation> + <xsd:documentation> + This includes brings in the common attributes for SCXML. + </xsd:documentation> + </xsd:annotation> + </xsd:include> + + <xsd:include schemaLocation="scxml-contentmodels.xsd"> + <xsd:annotation> + <xsd:documentation> + This includes the common content models. + </xsd:documentation> + </xsd:annotation> + </xsd:include> + + <xsd:include schemaLocation="scxml-datatypes.xsd"> + <xsd:annotation> + <xsd:documentation> + This includes brings in the common data types for SCXML. + </xsd:documentation> + </xsd:annotation> + </xsd:include> + + <xsd:redefine schemaLocation="scxml-module-data.xsd"> + <xsd:annotation> + <xsd:documentation> + This imports the data module for SCXML and redefines the following. + [1] Redefines assign attribute group to allow type and attr + </xsd:documentation> + </xsd:annotation> + <xsd:attributeGroup name="scxml.assign.attlist"> + <xsd:attributeGroup ref="scxml.assign.attlist"/> + <xsd:attribute name="type" type="AssignType.datatype" default="replacechildren"/> + <xsd:attribute name="attr" type="xsd:NMTOKEN"/> + </xsd:attributeGroup> + </xsd:redefine> + + <xsd:include schemaLocation="scxml-module-script.xsd"> + <xsd:annotation> + <xsd:documentation> + This includes the script module for SCXML. + </xsd:documentation> + </xsd:annotation> + </xsd:include> + + <xsd:redefine schemaLocation="scxml-module-external.xsd"> + <xsd:annotation> + <xsd:documentation> + This imports the external module for SCXML and redefines the following. + [1] Redefines send and invoke mix group to allow + param + [2] Redefines finalize mix group to allow: + executable content + </xsd:documentation> + </xsd:annotation> + <xsd:group name="scxml.send.mix"> + <xsd:choice> + <xsd:group ref="scxml.send.mix"/> + <xsd:element ref="param" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + </xsd:group> + <xsd:group name="scxml.invoke.mix"> + <xsd:choice> + <xsd:group ref="scxml.invoke.mix"/> + <xsd:element ref="param" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + </xsd:group> + <xsd:group name="scxml.finalize.mix"> + <xsd:choice> + <xsd:group ref="scxml.finalize.mix"/> + <xsd:group ref="scxml.core.executablecontent"/> + </xsd:choice> + </xsd:group> + </xsd:redefine> + + <xsd:redefine schemaLocation="scxml-module-core.xsd"> + <xsd:annotation> + <xsd:documentation> + This imports the core module for SCXML and redefines the following. + [1] Redefines executable content to allow + send, assign, validate, cancel and script elements + [2] Redefines state and parallel mix group to allow + invoke and datamodel + [3] Redefines scxml group to allow + datamodel and script + </xsd:documentation> + </xsd:annotation> + <xsd:group name="scxml.core.executablecontent"> + <xsd:choice> + <xsd:group ref="scxml.core.executablecontent"/> + <xsd:element ref="send"/> + <xsd:element ref="assign"/> + <xsd:element ref="script"/> + <xsd:element ref="validate"/> + <xsd:element ref="cancel"/> + </xsd:choice> + </xsd:group> + <xsd:group name="scxml.scxml.mix"> + <xsd:choice> + <xsd:group ref="scxml.scxml.mix"/> + <xsd:element ref="datamodel" minOccurs="0" maxOccurs="unbounded"/> + <xsd:element ref="script" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + </xsd:group> + <xsd:group name="scxml.state.mix"> + <xsd:choice> + <xsd:group ref="scxml.state.mix"/> + <xsd:element ref="datamodel" minOccurs="0" maxOccurs="unbounded"/> + <xsd:element ref="invoke" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + </xsd:group> + <xsd:group name="scxml.parallel.mix"> + <xsd:choice> + <xsd:group ref="scxml.parallel.mix"/> + <xsd:element ref="datamodel" minOccurs="0" maxOccurs="unbounded"/> + <xsd:element ref="invoke" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + </xsd:group> + <xsd:group name="scxml.donedata.content"> + <xsd:choice> + <xsd:group ref="scxml.donedata.content"/> + <xsd:element ref="param" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + </xsd:group> + + </xsd:redefine> + +</xsd:schema> diff --git a/test/src/test-apache-commons.cpp b/test/src/test-apache-commons.cpp new file mode 100644 index 0000000..51c275d --- /dev/null +++ b/test/src/test-apache-commons.cpp @@ -0,0 +1,128 @@ +#include "uscxml/Interpreter.h" +#include <glog/logging.h> + +using namespace uscxml; +using namespace Arabica::DOM; +using namespace Arabica::XPath; + +static std::string path; + +bool testEvents1() { + LOG(INFO) << "---- testEvent1 "; + Interpreter* interpreter = new Interpreter(path + "/eventdata-01.xml"); + interpreter->start(); + interpreter->waitForStabilization(); + assert(interpreter->getConfiguration().size() == 1); + assert(Interpreter::isMember(interpreter->getState("state1"), interpreter->getConfiguration())); + + Event eventFoo; + eventFoo.name = "event.foo"; + eventFoo.atom = "3"; + interpreter->receive(eventFoo); + interpreter->waitForStabilization(); + assert(interpreter->getConfiguration().size() == 1); + assert(Interpreter::isMember(interpreter->getState("state3"), interpreter->getConfiguration())); + + Event eventBar; + eventBar.name = "event.bar"; + eventBar.atom = "6"; + interpreter->receive(eventBar); + interpreter->waitForStabilization(); + assert(interpreter->getConfiguration().size() == 1); + assert(Interpreter::isMember(interpreter->getState("state6"), interpreter->getConfiguration())); + + Event eventBaz; + eventBaz.name = "event.baz"; + eventBaz.atom = "7"; + interpreter->receive(eventBaz); + + delete interpreter; + return true; +} + +bool testEvents2() { + LOG(INFO) << "---- testEvent2 "; + Interpreter* interpreter = new Interpreter(path + "/eventdata-02.xml"); + interpreter->start(); + interpreter->waitForStabilization(); + assert(interpreter->getConfiguration().size() == 1); + assert(Interpreter::isMember(interpreter->getState("state0"), interpreter->getConfiguration())); + + Event eventConnAlert; + eventConnAlert.name = "connection.alerting"; + eventConnAlert.atom = "'line2'"; + interpreter->receive(eventConnAlert); + interpreter->waitForStabilization(); + assert(interpreter->getConfiguration().size() == 1); + assert(Interpreter::isMember(interpreter->getState("state2"), interpreter->getConfiguration())); + + Event eventConnAlert2; + eventConnAlert2.name = "connection.alerting"; + eventConnAlert2.compound["line"] = Data(std::string("4")); + interpreter->receive(eventConnAlert2); + + delete interpreter; + return true; +} + +//bool testEvents3() { +// LOG(INFO) << "---- testEvent3 "; +// Interpreter* Interpreter = new Interpreter(path + "/eventdata-03.xml"); +// interpreter->start(); +// interpreter->waitForStabilization(); +// Thread::sleepMs(200); +// assert(interpreter->getConfiguration().size() == 1); +// assert(Interpreter::isMember(interpreter->getState("state0"), interpreter->getConfiguration())); +// +// Event eventConnAlert; +// eventConnAlert.name = "connection.alerting"; +// eventConnAlert.atom = "'line2'"; +// interpreter->receive(eventConnAlert); +// Thread::sleepMs(200); +// assert(interpreter->getConfiguration().size() == 1); +// assert(Interpreter::isMember(interpreter->getState("state2"), interpreter->getConfiguration())); +// +// Event eventConnAlert2; +// eventConnAlert2.name = "connection.alerting"; +// eventConnAlert2.compound["line"] = Data(std::string("4")); +// interpreter->receive(eventConnAlert2); +// Thread::sleepMs(200); +// assert(interpreter->getConfiguration().size() == 1); +// assert(Interpreter::isMember(interpreter->getState("state4"), interpreter->getConfiguration())); +// +// delete Interpreter; +// return true; +//} + + +int main(int argc, char** argv) { + if (argc != 2) { + std::cerr << "Expected path to scxml file from apache commons distribution" << std::endl; + exit(EXIT_FAILURE); + } + + path = "file://"; + path += argv[1]; + + if (!testEvents1()) + return EXIT_FAILURE; + if (!testEvents2()) + return EXIT_FAILURE; +// if (!testEvents3()) +// return EXIT_FAILURE; + +// +// Interpreter* scxmlInterpreter = new Interpreter(path + "/tie-breaker-01.xml"); +// SCXMLRunner* scxmlRun = new SCXMLRunner(scxmlInterpreter); +// scxmlRun->start(); +// +// Thread::sleepMs(100); +// assert(Interpreter::isMember(scxmlinterpreter->getState("ten"), scxmlinterpreter->getConfiguration())); +// +// boost::shared_ptr<Event> event = boost::shared_ptr<Event>(new Event()); +// event->name = "ten.done"; +// scxmlinterpreter->receive(event); +// scxmlRun->join(); +// scxmlinterpreter->receive(event); + +}
\ No newline at end of file diff --git a/test/src/test-communication.cpp b/test/src/test-communication.cpp new file mode 100644 index 0000000..97584b8 --- /dev/null +++ b/test/src/test-communication.cpp @@ -0,0 +1,26 @@ +#include "uscxml/Interpreter.h" +#include <DOM/io/Stream.hpp> + +int main(int argc, char** argv) { + if (argc != 2) { + std::cerr << "Expected path to test-communication.scxml" << std::endl; + exit(EXIT_FAILURE); + } + + + using namespace uscxml; + std::list<Interpreter*> _interpreters; + +// Event e; +// e.compound["foo"] = Data("bar", Data::VERBATIM); +// e.compound["foo2"] = Data("bar2", Data::VERBATIM); +// std::cout << e.toDocument() << std::endl; + + for (int i = 0; i < 1; i++) { + _interpreters.push_back(new Interpreter(argv[1])); + _interpreters.back()->start(); + } + + tthread::this_thread::sleep_for(tthread::chrono::milliseconds(100000)); + +}
\ No newline at end of file diff --git a/test/src/test-communication.scxml b/test/src/test-communication.scxml new file mode 100644 index 0000000..cc3f577 --- /dev/null +++ b/test/src/test-communication.scxml @@ -0,0 +1,35 @@ +<scxml datamodel="ecmascript" initial="start" binding="late" name="foo"> + <state id="start"> + <datamodel> + <data id="foo" expr="'this is the foo data'" /> + <data id="bar" expr="'this is the bar data'" /> + </datamodel> + <onentry> + <log expr="'basichttp listening as ' + _ioprocessors['basichttp']" /> + <log expr="'Entered step1'" /> + <log expr="'Sending ourself an event'" /> + <send targetexpr="_ioprocessors['basichttp']" namelist="foo" type="basichttp" event="transitionToNext"> + <param name="bar" expr="'b' + 'ar'" /> + <content> +<![CDATA[ +This is some content you got there dude! +]]> + </content> + </send> + </onentry> + <transition event="transitionToNext" target="step1" /> + </state> + <state id="step1"> + <onentry> + <log expr="'Entered step1'" /> + <log expr="'Sending ourself a 2s delayed event'" /> + <send delay="2s" targetexpr="_ioprocessors['basichttp']" type="basichttp" event="transitionToNext" /> + </onentry> + <transition event="transitionToNext" target="final" /> + </state> + <final id="final"> + <onentry> + <log expr="'Finished!'" /> + </onentry> + </final> +</scxml>
\ No newline at end of file diff --git a/test/src/test-ecmascript-v8.cpp b/test/src/test-ecmascript-v8.cpp new file mode 100644 index 0000000..d0b69ba --- /dev/null +++ b/test/src/test-ecmascript-v8.cpp @@ -0,0 +1,44 @@ +#include "uscxml/Interpreter.h" +#include "uscxml/datamodel/ecmascript/v8/V8DataModel.h" + +int main(int argc, char** argv) { + if (argc != 2) { + std::cerr << "Expected path to test-ecmascript.scxml" << std::endl; + exit(EXIT_FAILURE); + } + + using namespace uscxml; + using namespace Arabica::DOM; + using namespace Arabica::XPath; + +// class SCXMLRunner : public Thread { +// public: +// SCXMLRunner(Runtime* runtime) : _runtime(runtime) {} +// void run() { +// _runtime->interpret(); +// } +// +// Runtime* _runtime; +// }; + +// boost::shared_ptr<V8DataModel> v8 = boost::static_pointer_cast<V8DataModel>(Factory::create("datamodel:ecmascript", Arabica::DOM::Node<std::string>())); +// v8->eval("var x = 4;"); +// assert(v8->evalAsBool("x == 4")); +// assert(!v8->evalAsBool("x == 5")); + + Interpreter* scxml = new Interpreter(argv[1]); + scxml->dump(); +// scxml->interpret(); + scxml->start(); + scxml->waitForStabilization(); + + Event event1; + event1.name = "event1"; + scxml->receive(event1); + scxml->waitForStabilization(); + tthread::this_thread::sleep_for(tthread::chrono::milliseconds(200)); + +// SCXMLRunner* scxmlRun = new SCXMLRunner(scxmlRuntime); +// scxmlRun->start(); + +}
\ No newline at end of file diff --git a/test/src/test-ecmascript.scxml b/test/src/test-ecmascript.scxml new file mode 100644 index 0000000..aa88f17 --- /dev/null +++ b/test/src/test-ecmascript.scxml @@ -0,0 +1,112 @@ +<scxml datamodel="ecmascript" initial="comparison" binding="late"> + <datamodel> + <data id="year" expr="2008" /> + <data id="CEO" expr="'Mr Big'" /> + <data id="profitable" /> + <data id="json"> + { + "id": 1, + "name": "Foo", + "price": 123, + "tags": [ "Bar", "Eek" ], + "stock": { + "warehouse": 300, + "retail": 20, + } + } + </data> + </datamodel> + <script> + var x = 4; + var a = ["a", "b", "c"]; + var b = [10,20,30]; + </script> + <script> + var y; + if (x > 10) { + y = 'true'; + } else { + y = 'false'; + } + </script> + <state id="comparison"> + <onentry> + <log expr="'-- Testing comparisons'" /> + <log expr="'x is ' + x + ', y is ' + y" /> + </onentry> + <transition cond="x >= 2" target="builtin"> + <log expr="'x is greater or equal to 2'" /> + </transition> + <transition cond="x < 2" target="builtin"> + <log expr="'x is smaller than 2'" /> + </transition> + </state> + <state id="builtin"> + <onentry> + <log expr="'-- Testing built ins'" /> + <if cond="In('builtin')"> + <log expr="'We are in state builtin'" /> + <else> + <log expr="'We are not in state builtin'" /> + </else> + </if> + </onentry> + <transition target="conditionals" /> + </state> + <state id="conditionals"> + <onentry> + <log expr="'-- Testing conditionals'" /> + <if cond="y == true"> + <log expr="'x is great and y is'" /> + <elseif cond="x > 3"> + <log expr="'x is somewhat great and y is not'" /> + </elseif> + <else> + <log expr="'x is great and y is not'" /> + </else> + </if> + </onentry> + <transition target="foreach" /> + </state> + <state id="foreach"> + <onentry> + <log expr="'-- Testing loops'" /> + <foreach array="a" item="itemA" index="indexA"> + <foreach array="b" item="itemB" index="indexB"> + <log expr="indexA + '.' + indexB + ' = ' + itemA + '.' + itemB" /> + </foreach> + </foreach> + </onentry> + <transition target="externalEvents" /> + </state> + <state id="externalEvents"> + <onentry> + <log expr="'-- External Events'" /> + </onentry> + <transition target="datamodels" event="event1" cond="_event.name == 'event1'" /> + </state> + <state id="datamodels"> + <datamodel> + <data id="bar" expr="'yeah, bar!'"/> + </datamodel> + <onentry> + <log expr="'-- DataModels'" /> + <log expr="'year = ' + year" /> + <log expr="'bar = ' + bar" /> + <log expr="'json.stock.warehouse = ' + json.stock.warehouse" /> + </onentry> + <transition target="syntaxerror" /> + </state> + <state id="syntaxerror"> + <onentry> + <log expr="'-- Syntax Error'" /> + <log expr="year = ' + year" /> + </onentry> + <transition event="error.execution" target="final" /> + </state> + <final id="final"> + <onentry> + <log expr="'Finished!'" /> + </onentry> + </final> +</scxml>
\ No newline at end of file diff --git a/test/src/test-eventdelay.cpp b/test/src/test-eventdelay.cpp new file mode 100644 index 0000000..6a28e44 --- /dev/null +++ b/test/src/test-eventdelay.cpp @@ -0,0 +1,39 @@ +#include "uscxml/concurrency/eventqueue/libevent/DelayedEventQueue.h" + +int eventCalled = 0; + +#include <sstream> + +static void callback(void* userData, const std::string eventId) { +// std::cout << eventId << ": " << (const char*)userData << std::endl; + std::cout << eventId << std::endl << std::flush; + eventCalled++; +} + +int main(int argc, char** argv) { + + using namespace uscxml; + DelayedEventQueue* eq = new DelayedEventQueue(); + + std::cout << "Starting" << std::endl; + eq->start(); + tthread::this_thread::sleep_for(tthread::chrono::milliseconds(10)); + +// eq->addEvent("foo", callback, 200, (void*)"event foo"); +// eq->addEvent("bar", callback, 400, (void*)"event bar"); +// eq->addEvent("bar", callback, 600, (void*)"event bar"); +// eq->cancelEvent("bar"); +// eq->addEvent("bar", callback, 300, (void*)"event bar"); +// eq->addEvent("baz", callback, 400, (void*)"event baz"); + + for (unsigned int i = 0; i <= 5000; i += 500) { +// eq->stop(); + std::stringstream ss; + ss << i; + eq->addEvent(ss.str(), callback, i, NULL); + std::cout << "Added " << i << std::endl; +// eq->start(); + } + tthread::this_thread::sleep_for(tthread::chrono::milliseconds(20000)); + +}
\ No newline at end of file diff --git a/test/src/test-execution.cpp b/test/src/test-execution.cpp new file mode 100644 index 0000000..e484eb4 --- /dev/null +++ b/test/src/test-execution.cpp @@ -0,0 +1,16 @@ +#include "uscxml/Interpreter.h" + +int main(int argc, char** argv) { + if (argc != 2) { + std::cerr << "Expected path to test-execution.scxml" << std::endl; + exit(EXIT_FAILURE); + } + + using namespace uscxml; + using namespace Arabica::DOM; + using namespace Arabica::XPath; + + Interpreter* interpreter = new Interpreter(argv[1]); + interpreter->dump(); + interpreter->interpret(); +}
\ No newline at end of file diff --git a/test/src/test-execution.scxml b/test/src/test-execution.scxml new file mode 100644 index 0000000..ada1a17 --- /dev/null +++ b/test/src/test-execution.scxml @@ -0,0 +1,56 @@ +<scxml initial="step2"> + <state id="start"> + <onentry> + <log expr="'Entered State: start'" /> + </onentry> + <transition target="step1"> + <log expr="'Transition start -> step1'" /> + </transition> + <onexit> + <log expr="'Exited State: start'" /> + </onexit> + </state> + <state id="step1"> + <onentry> + <log expr="'Entered State: step1'" /> + </onentry> + <transition target="step2"> + <log expr="'Transition step1 -> step2'" /> + </transition> + <onexit> + <log expr="'Exited State: step1'" /> + </onexit> + </state> + <parallel id="step2"> + <state id="parallel1"> + <onentry> + <log expr="'Entered State: parallel1'" /> + </onentry> + <transition target="step3"> + <log expr="'Transition parallel1 -> step3'" /> + </transition> + <onexit> + <log expr="'Exited State: parallel1'" /> + </onexit> + </state> + <state id="parallel2"> + <onentry> + <log expr="'Entered State: parallel2'" /> + </onentry> + <transition target="step3"> + <log expr="'Transition parallel2 -> step3'" /> + </transition> + <onexit> + <log expr="'Exited State: parallel2'" /> + </onexit> + </state> + </parallel> + <final id="step3"> + <onentry> + <log expr="'Entered Final State: step3'" /> + </onentry> + <onexit> + <log expr="'Exited Final State: step3'" /> + </onexit> + </final> +</scxml>
\ No newline at end of file diff --git a/test/src/test-predicates.cpp b/test/src/test-predicates.cpp new file mode 100644 index 0000000..73c37dc --- /dev/null +++ b/test/src/test-predicates.cpp @@ -0,0 +1,60 @@ +#define protected public +#include "uscxml/Interpreter.h" +#undef protected + +int main(int argc, char** argv) { + if (argc != 2) { + std::cerr << "Expected path to test-predicates.scxml" << std::endl; + exit(EXIT_FAILURE); + } + + using namespace uscxml; + using namespace Arabica::DOM; + using namespace Arabica::XPath; + + Interpreter* interpreter = new Interpreter(argv[1]); + + Node<std::string> atomicState = interpreter->getState("atomic"); + assert(Interpreter::isAtomic(atomicState)); + assert(!Interpreter::isParallel(atomicState)); + assert(!Interpreter::isCompound(atomicState)); + + Node<std::string> compoundState = interpreter->getState("compound"); + assert(!Interpreter::isAtomic(compoundState)); + assert(!Interpreter::isParallel(compoundState)); + assert(Interpreter::isCompound(compoundState)); + + Node<std::string> parallelState = interpreter->getState("parallel"); + assert(!Interpreter::isAtomic(parallelState)); + assert(Interpreter::isParallel(parallelState)); + assert(!Interpreter::isCompound(parallelState)); // parallel states are not compound! + + Node<std::string> initialState = interpreter->getInitialState(); + assert(initialState == atomicState); + + NodeSet<std::string> childs = interpreter->getChildStates(compoundState); + Node<std::string> compundChild1 = interpreter->getState("compundChild1"); + Node<std::string> compundChild2 = interpreter->getState("compundChild2"); + assert(childs.size() > 0); + assert(Interpreter::isMember(compundChild1, childs)); + assert(Interpreter::isMember(compundChild2, childs)); + assert(!Interpreter::isMember(compoundState, childs)); + + assert(Interpreter::isDescendant(compundChild1, compoundState)); + + std::string transEvents; + transEvents = "error"; + assert(Interpreter::nameMatch(transEvents, "error")); + assert(!Interpreter::nameMatch(transEvents, "foo")); + + transEvents = "error foo"; + assert(Interpreter::nameMatch(transEvents, "error")); + assert(Interpreter::nameMatch(transEvents, "error.send")); + assert(Interpreter::nameMatch(transEvents, "error.send.failed")); + assert(Interpreter::nameMatch(transEvents, "foo")); + assert(Interpreter::nameMatch(transEvents, "foo.bar")); + assert(!Interpreter::nameMatch(transEvents, "errors.my.custom")); + assert(!Interpreter::nameMatch(transEvents, "errorhandler.mistake")); + assert(!Interpreter::nameMatch(transEvents, "errOr.send")); + assert(!Interpreter::nameMatch(transEvents, "foobar")); +}
\ No newline at end of file diff --git a/test/src/test-predicates.scxml b/test/src/test-predicates.scxml new file mode 100644 index 0000000..98848a2 --- /dev/null +++ b/test/src/test-predicates.scxml @@ -0,0 +1,9 @@ +<scxml> + <state id="atomic" /> + <state id="compound"> + <state id="compundChild1" /> + <state id="compundChild2" /> + </state> + <parallel id="parallel"> + </parallel> +</scxml>
\ No newline at end of file |