summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt8
-rw-r--r--contrib/cmake/CPackUSCXML.cmake2
-rw-r--r--contrib/cmake/FindSqlite3.cmake0
-rw-r--r--contrib/src/getopt/XGetopt.cpp (renamed from contrib/snippets/XGetopt.cpp)0
-rw-r--r--contrib/src/getopt/XGetopt.h (renamed from contrib/snippets/XGetopt.h)0
-rw-r--r--contrib/src/jsmn/LICENSE20
-rw-r--r--contrib/src/jsmn/Makefile26
-rw-r--r--contrib/src/jsmn/README157
-rw-r--r--contrib/src/jsmn/jsmn.c255
-rw-r--r--contrib/src/jsmn/jsmn.h67
-rw-r--r--contrib/src/jsmn/jsmn_test.c364
-rw-r--r--src/uscxml/Interpreter.cpp480
-rw-r--r--src/uscxml/Interpreter.h39
-rw-r--r--src/uscxml/Message.cpp106
-rw-r--r--src/uscxml/Message.h1
-rw-r--r--src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp16
-rw-r--r--src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h21
-rw-r--r--test/CMakeLists.txt33
-rwxr-xr-xtest/run-scxml-test-framework.sh166
-rw-r--r--test/samples/uscxml/test-execution.scxml6
-rw-r--r--test/schema/scxml-attribs.xsd57
-rw-r--r--test/schema/scxml-contentmodels.xsd35
-rw-r--r--test/schema/scxml-datatypes.xsd203
-rw-r--r--test/schema/scxml-message.xsd122
-rw-r--r--test/schema/scxml-module-core.xsd405
-rw-r--r--test/schema/scxml-module-data.xsd151
-rw-r--r--test/schema/scxml-module-external.xsd152
-rw-r--r--test/schema/scxml.xsd138
-rw-r--r--test/src/scxml-test-framework-client.cpp147
-rw-r--r--test/src/test-url.cpp19
30 files changed, 2571 insertions, 625 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b6fe9ad..8a5588f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -136,6 +136,7 @@ include_directories(${USCXML_PREBUILT_LIBRARY_PATH}/include)
if (WIN32)
include_directories(${PROJECT_SOURCE_DIR}/contrib/snippets)
endif()
+include_directories(${PROJECT_SOURCE_DIR}/contrib/src/jsmn/)
############################################################
# General setup
@@ -215,8 +216,8 @@ elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
# SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB")
# SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:MSVCRTD.lib")
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
- add_definitions("-Wno-parentheses-equality")
- set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-fPIC")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-parentheses-equality")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
# set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -read_only_relocs suppress")
# set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -read_only_relocs suppress")
else()
@@ -380,6 +381,7 @@ source_group("Interpreter" FILES ${USCXML_DEBUG})
list (APPEND USCXML_FILES ${USCXML_DEBUG})
file(GLOB USCXML_CORE
+ contrib/src/jsmn/jsmn.c
src/uscxml/*.cpp
src/uscxml/*.h
)
@@ -721,7 +723,7 @@ if (NOT BUILD_AS_PLUGINS)
endif()
target_link_libraries(uscxml ${USCXML_OPT_LIBS} ${USCXML_CORE_LIBS})
-add_executable(mmi-browser apps/mmi-browser.cpp ${PROJECT_SOURCE_DIR}/contrib/snippets/XGetopt.cpp)
+add_executable(mmi-browser apps/mmi-browser.cpp ${PROJECT_SOURCE_DIR}/contrib/src/getopt/XGetopt.cpp)
target_link_libraries(mmi-browser uscxml)
set_target_properties(mmi-browser PROPERTIES FOLDER "Apps")
install_executable(TARGETS mmi-browser COMPONENT tools)
diff --git a/contrib/cmake/CPackUSCXML.cmake b/contrib/cmake/CPackUSCXML.cmake
index 63cfa32..943dbc3 100644
--- a/contrib/cmake/CPackUSCXML.cmake
+++ b/contrib/cmake/CPackUSCXML.cmake
@@ -270,7 +270,7 @@ list (APPEND USCXML_CPACK_COMPONENTS "headers")
if (NOT CMAKE_CROSS_COMPILING)
list (APPEND USCXML_CPACK_COMPONENTS "tools")
endif()
-list (REMOVE_DUPLICATES USCXML_CPACK_COMPONENTS ${USCXML_CPACK_COMPONENTS})
+list (REMOVE_DUPLICATES USCXML_CPACK_COMPONENTS)
#message("USCXML_CPACK_COMPONENTS: ${USCXML_CPACK_COMPONENTS}")
########################################
diff --git a/contrib/cmake/FindSqlite3.cmake b/contrib/cmake/FindSqlite3.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/cmake/FindSqlite3.cmake
diff --git a/contrib/snippets/XGetopt.cpp b/contrib/src/getopt/XGetopt.cpp
index 373a5db..373a5db 100644
--- a/contrib/snippets/XGetopt.cpp
+++ b/contrib/src/getopt/XGetopt.cpp
diff --git a/contrib/snippets/XGetopt.h b/contrib/src/getopt/XGetopt.h
index 7e75f26..7e75f26 100644
--- a/contrib/snippets/XGetopt.h
+++ b/contrib/src/getopt/XGetopt.h
diff --git a/contrib/src/jsmn/LICENSE b/contrib/src/jsmn/LICENSE
new file mode 100644
index 0000000..c84fb2e
--- /dev/null
+++ b/contrib/src/jsmn/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2010 Serge A. Zaitsev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/contrib/src/jsmn/Makefile b/contrib/src/jsmn/Makefile
new file mode 100644
index 0000000..0afdef4
--- /dev/null
+++ b/contrib/src/jsmn/Makefile
@@ -0,0 +1,26 @@
+# You can put your build options here
+-include config.mk
+
+all: libjsmn.a
+
+libjsmn.a: jsmn.o
+ $(AR) rc $@ $^
+
+%.o: %.c jsmn.h
+ $(CC) -c $(CFLAGS) $< -o $@
+
+test: jsmn_test
+ ./jsmn_test
+
+jsmn_test: jsmn_test.o
+ $(CC) -L. -ljsmn $< -o $@
+
+jsmn_test.o: jsmn_test.c libjsmn.a
+
+clean:
+ rm -f jsmn.o jsmn_test.o
+ rm -f jsmn_test
+ rm -f libjsmn.a
+
+.PHONY: all clean test
+
diff --git a/contrib/src/jsmn/README b/contrib/src/jsmn/README
new file mode 100644
index 0000000..c6e7686
--- /dev/null
+++ b/contrib/src/jsmn/README
@@ -0,0 +1,157 @@
+
+JSMN
+====
+
+jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be
+easily integrated into resource-limited or embedded projects.
+
+You can find more information about JSON format at [json.org][1]
+
+Library sources are available at [bitbucket.org/zserge/jsmn][2]
+
+Philosophy
+----------
+
+Most JSON parsers offer you a bunch of functions to load JSON data, parse it
+and extract any value by its name. jsmn proves that checking the correctness of
+every JSON packet or allocating temporary objects to store parsed JSON fields
+often is an overkill.
+
+JSON format itself is extremely simple, so why should we complicate it?
+
+jsmn is designed to be **robust** (it should work fine even with erroneous
+data), **fast** (it should parse data on the fly), **portable** (no superfluous
+dependencies or non-standard C extensions). An of course, **simplicity** is a
+key feature - simple code style, simple algorithm, simple integration into
+other projects.
+
+Features
+--------
+
+* compatible with C89
+* no dependencies (even libc!)
+* highly portable (tested on x86/amd64, ARM, AVR)
+* about 200 lines of code
+* extremely small code footprint
+* API contains only 2 functions
+* no dynamic memory allocation
+* incremental single-pass parsing
+* library code is covered with unit-tests
+
+Design
+------
+
+The rudimentary jsmn object is a **token**. Let's consider a JSON string:
+
+ '{ "name" : "Jack", "age" : 27 }'
+
+It holds the following tokens:
+
+* Object: `{ "name" : "Jack", "age" : 27}` (the whole object)
+* Strings: `"name"`, `"Jack"`, `"age"` (keys and some values)
+* Number: `27`
+
+In jsmn, tokens do not hold any data, but point to token boundaries in JSON
+string instead. In the example above jsmn will create tokens like: Object
+[0..31], String [3..7], String [12..16], String [20..23], Number [27..29].
+
+Every jsmn token has a type, which indicates the type of corresponding JSON
+token. jsmn supports the following token types:
+
+* Object - a container of key-value pairs, e.g.:
+ `{ "foo":"bar", "x":0.3 }`
+* Array - a sequence of values, e.g.:
+ `[ 1, 2, 3 ]`
+* String - a quoted sequence of chars, e.g.: `"foo"`
+* Primitive - a number, a boolean (`true`, `false`) or `null`
+
+Besides start/end positions, jsmn tokens for complex types (like arrays
+or objects) also contain a number of child items, so you can easily follow
+object hierarchy.
+
+This approach provides enough information for parsing any JSON data and makes
+it possible to use zero-copy techniques.
+
+Install
+-------
+
+To clone the repository you should have mercurial installed. Just run:
+
+ $ hg clone http://bitbucket.org/zserge/jsmn jsmn
+
+Repository layout is simple: jsmn.c and jsmn.h are library files; demo.c is an
+example of how to use jsmn (it is also used in unit tests); test.sh is a test
+script. You will also find README, LICENSE and Makefile files inside.
+
+To build the library, run `make`. It is also recommended to run `make test`.
+Let me know, if some tests fail.
+
+If build was successful, you should get a `libjsmn.a` library.
+The header file you should include is called `"jsmn.h"`.
+
+API
+---
+
+Token types are described by `jsmntype_t`:
+
+ typedef enum {
+ JSMN_OBJECT,
+ JSMN_ARRAY,
+ JSMN_STRING,
+ JSMN_PRIMITIVE
+ } jsmntype_t;
+
+**Note:** Unlike JSON data types, primitive tokens are not divided into
+numbers, booleans and null, because one can easily tell the type using the
+first character:
+
+* <code>'t', 'f'</code> - boolean
+* <code>'n'</code> - null
+* <code>'-', '0'..'9'</code> - number
+
+Token is an object of `jsmntok_t` type:
+
+ typedef struct {
+ jsmntype_t type; // Token type
+ int start; // Token start position
+ int end; // Token end position
+ int size; // Number of child (nested) tokens
+ } jsmntok_t;
+
+**Note:** string tokens point to the first character after
+the opening quote and the previous symbol before final quote. This was made
+to simplify string extraction from JSON data.
+
+All job is done by `jsmn_parser` object. You can initialize a new parser using:
+
+ struct jsmn_parser parser;
+ jsmntok_t tokens[10];
+
+ // js - pointer to JSON string
+ // tokens - an array of tokens available
+ // 10 - number of tokens available
+ jsmn_init_parser(&parser, js, tokens, 10);
+
+This will create a parser, that can parse up to 10 JSON tokens from `js` string.
+
+Later, you can use `jsmn_parse(&parser)` function to process JSON string with the parser.
+If something goes wrong, you will get an error. Error will be one of these:
+
+* `JSMN_SUCCESS` - everything went fine. String was parsed
+* `JSMN_ERROR_INVAL` - bad token, JSON string is corrupted
+* `JSMN_ERROR_NOMEM` - not enough tokens, JSON string is too large
+* `JSMN_ERROR_PART` - JSON string is too short, expecting more JSON data
+
+If you get `JSON_ERROR_NOMEM`, you can re-allocate more tokens and call
+`jsmn_parse` once more. If you read json data from the stream, you can
+periodically call `jsmn_parse` and check if return value is `JSON_ERROR_PART`.
+You will get this error until you reach the end of JSON data.
+
+Other info
+----------
+
+This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php),
+ so feel free to integrate it in your commercial products.
+
+[1]: http://www.json.org/
+[2]: https://bitbucket.org/zserge/jsmn/wiki/Home
diff --git a/contrib/src/jsmn/jsmn.c b/contrib/src/jsmn/jsmn.c
new file mode 100644
index 0000000..1b918f5
--- /dev/null
+++ b/contrib/src/jsmn/jsmn.c
@@ -0,0 +1,255 @@
+#include <stdlib.h>
+
+#include "jsmn.h"
+
+/**
+ * Allocates a fresh unused token from the token pull.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
+ jsmntok_t *tokens, size_t num_tokens) {
+ jsmntok_t *tok;
+ if (parser->toknext >= num_tokens) {
+ return NULL;
+ }
+ tok = &tokens[parser->toknext++];
+ tok->start = tok->end = -1;
+ tok->size = 0;
+#ifdef JSMN_PARENT_LINKS
+ tok->parent = -1;
+#endif
+ return tok;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
+ int start, int end) {
+ token->type = type;
+ token->start = start;
+ token->end = end;
+ token->size = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
+ jsmntok_t *tokens, size_t num_tokens) {
+ jsmntok_t *token;
+ int start;
+
+ start = parser->pos;
+
+ for (; js[parser->pos] != '\0'; parser->pos++) {
+ switch (js[parser->pos]) {
+#ifndef JSMN_STRICT
+ /* In strict mode primitive must be followed by "," or "}" or "]" */
+ case ':':
+#endif
+ case '\t' : case '\r' : case '\n' : case ' ' :
+ case ',' : case ']' : case '}' :
+ goto found;
+ }
+ if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+#ifdef JSMN_STRICT
+ /* In strict mode primitive must be followed by a comma/object/array */
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+#endif
+
+found:
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ parser->pos--;
+ return JSMN_SUCCESS;
+}
+
+/**
+ * Filsl next token with JSON string.
+ */
+static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
+ jsmntok_t *tokens, size_t num_tokens) {
+ jsmntok_t *token;
+
+ int start = parser->pos;
+
+ parser->pos++;
+
+ /* Skip starting quote */
+ for (; js[parser->pos] != '\0'; parser->pos++) {
+ char c = js[parser->pos];
+
+ /* Quote: end of string */
+ if (c == '\"') {
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ return JSMN_SUCCESS;
+ }
+
+ /* Backslash: Quoted symbol expected */
+ if (c == '\\') {
+ parser->pos++;
+ switch (js[parser->pos]) {
+ /* Allowed escaped symbols */
+ case '\"': case '/' : case '\\' : case 'b' :
+ case 'f' : case 'r' : case 'n' : case 't' :
+ break;
+ /* Allows escaped symbol \uXXXX */
+ case 'u':
+ /* TODO */
+ break;
+ /* Unexpected symbol */
+ default:
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+ }
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens,
+ unsigned int num_tokens) {
+ jsmnerr_t r;
+ int i;
+ jsmntok_t *token;
+
+ for (; js[parser->pos] != '\0'; parser->pos++) {
+ char c;
+ jsmntype_t type;
+
+ c = js[parser->pos];
+ switch (c) {
+ case '{': case '[':
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL)
+ return JSMN_ERROR_NOMEM;
+ if (parser->toksuper != -1) {
+ tokens[parser->toksuper].size++;
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ }
+ token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+ token->start = parser->pos;
+ parser->toksuper = parser->toknext - 1;
+ break;
+ case '}': case ']':
+ type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+#ifdef JSMN_PARENT_LINKS
+ if (parser->toknext < 1) {
+ return JSMN_ERROR_INVAL;
+ }
+ token = &tokens[parser->toknext - 1];
+ for (;;) {
+ if (token->start != -1 && token->end == -1) {
+ if (token->type != type) {
+ return JSMN_ERROR_INVAL;
+ }
+ token->end = parser->pos + 1;
+ parser->toksuper = token->parent;
+ break;
+ }
+ if (token->parent == -1) {
+ break;
+ }
+ token = &tokens[token->parent];
+ }
+#else
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ if (token->type != type) {
+ return JSMN_ERROR_INVAL;
+ }
+ parser->toksuper = -1;
+ token->end = parser->pos + 1;
+ break;
+ }
+ }
+ /* Error if unmatched closing bracket */
+ if (i == -1) return JSMN_ERROR_INVAL;
+ for (; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ parser->toksuper = i;
+ break;
+ }
+ }
+#endif
+ break;
+ case '\"':
+ r = jsmn_parse_string(parser, js, tokens, num_tokens);
+ if (r < 0) return r;
+ if (parser->toksuper != -1)
+ tokens[parser->toksuper].size++;
+ break;
+ case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ':
+ break;
+#ifdef JSMN_STRICT
+ /* In strict mode primitives are: numbers and booleans */
+ case '-': case '0': case '1' : case '2': case '3' : case '4':
+ case '5': case '6': case '7' : case '8': case '9':
+ case 't': case 'f': case 'n' :
+#else
+ /* In non-strict mode every unquoted value is a primitive */
+ default:
+#endif
+ r = jsmn_parse_primitive(parser, js, tokens, num_tokens);
+ if (r < 0) return r;
+ if (parser->toksuper != -1)
+ tokens[parser->toksuper].size++;
+ break;
+
+#ifdef JSMN_STRICT
+ /* Unexpected char in strict mode */
+ default:
+ return JSMN_ERROR_INVAL;
+#endif
+
+ }
+ }
+
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ /* Unmatched opened object or array */
+ if (tokens[i].start != -1 && tokens[i].end == -1) {
+ return JSMN_ERROR_PART;
+ }
+ }
+
+ return JSMN_SUCCESS;
+}
+
+/**
+ * Creates a new parser based over a given buffer with an array of tokens
+ * available.
+ */
+void jsmn_init(jsmn_parser *parser) {
+ parser->pos = 0;
+ parser->toknext = 0;
+ parser->toksuper = -1;
+}
+
diff --git a/contrib/src/jsmn/jsmn.h b/contrib/src/jsmn/jsmn.h
new file mode 100644
index 0000000..03b2c1a
--- /dev/null
+++ b/contrib/src/jsmn/jsmn.h
@@ -0,0 +1,67 @@
+#ifndef __JSMN_H_
+#define __JSMN_H_
+
+/**
+ * JSON type identifier. Basic types are:
+ * o Object
+ * o Array
+ * o String
+ * o Other primitive: number, boolean (true/false) or null
+ */
+typedef enum {
+ JSMN_PRIMITIVE = 0,
+ JSMN_OBJECT = 1,
+ JSMN_ARRAY = 2,
+ JSMN_STRING = 3
+} jsmntype_t;
+
+typedef enum {
+ /* Not enough tokens were provided */
+ JSMN_ERROR_NOMEM = -1,
+ /* Invalid character inside JSON string */
+ JSMN_ERROR_INVAL = -2,
+ /* The string is not a full JSON packet, more bytes expected */
+ JSMN_ERROR_PART = -3,
+ /* Everything was fine */
+ JSMN_SUCCESS = 0
+} jsmnerr_t;
+
+/**
+ * JSON token description.
+ * @param type type (object, array, string etc.)
+ * @param start start position in JSON data string
+ * @param end end position in JSON data string
+ */
+typedef struct {
+ jsmntype_t type;
+ int start;
+ int end;
+ int size;
+#ifdef JSMN_PARENT_LINKS
+ int parent;
+#endif
+} jsmntok_t;
+
+/**
+ * JSON parser. Contains an array of token blocks available. Also stores
+ * the string being parsed now and current position in that string
+ */
+typedef struct {
+ unsigned int pos; /* offset in the JSON string */
+ int toknext; /* next token to allocate */
+ int toksuper; /* superior token node, e.g parent object or array */
+} jsmn_parser;
+
+/**
+ * Create JSON parser over an array of tokens
+ */
+void jsmn_init(jsmn_parser *parser);
+
+/**
+ * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
+ * a single JSON object.
+ */
+jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js,
+ jsmntok_t *tokens, unsigned int num_tokens);
+
+#endif /* __JSMN_H_ */
diff --git a/contrib/src/jsmn/jsmn_test.c b/contrib/src/jsmn/jsmn_test.c
new file mode 100644
index 0000000..308faf6
--- /dev/null
+++ b/contrib/src/jsmn/jsmn_test.c
@@ -0,0 +1,364 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "jsmn.c"
+
+static int test_passed = 0;
+static int test_failed = 0;
+
+/* Terminate current test with error */
+#define fail() return __LINE__
+
+/* Successfull end of the test case */
+#define done() return 0
+
+/* Check single condition */
+#define check(cond) do { if (!(cond)) fail(); } while (0)
+
+/* Test runner */
+static void test(int (*func)(void), const char *name) {
+ int r = func();
+ if (r == 0) {
+ test_passed++;
+ } else {
+ test_failed++;
+ printf("FAILED: %s (at line %d)\n", name, r);
+ }
+}
+
+#define TOKEN_EQ(t, tok_start, tok_end, tok_type) \
+ ((t).start == tok_start \
+ && (t).end == tok_end \
+ && (t).type == (tok_type))
+
+#define TOKEN_STRING(js, t, s) \
+ (strncmp(js+(t).start, s, (t).end - (t).start) == 0 \
+ && strlen(s) == (t).end - (t).start)
+
+#define TOKEN_PRINT(t) \
+ printf("start: %d, end: %d, type: %d, size: %d\n", \
+ (t).start, (t).end, (t).type, (t).size)
+
+int test_empty() {
+ const char *js;
+ int r;
+ jsmn_parser p;
+ jsmntok_t t[10];
+
+ js = "{}";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, t, 10);
+ check(r == JSMN_SUCCESS);
+ check(t[0].type == JSMN_OBJECT);
+ check(t[0].start == 0 && t[0].end == 2);
+
+ js = "[]";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, t, 10);
+ check(r == JSMN_SUCCESS);
+ check(t[0].type == JSMN_ARRAY);
+ check(t[0].start == 0 && t[0].end == 2);
+
+ js = "{\"a\":[]}";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, t, 10);
+ check(r == JSMN_SUCCESS);
+ check(t[0].type == JSMN_OBJECT && t[0].start == 0 && t[0].end == 8);
+ check(t[1].type == JSMN_STRING && t[1].start == 2 && t[1].end == 3);
+ check(t[2].type == JSMN_ARRAY && t[2].start == 5 && t[2].end == 7);
+
+ js = "[{},{}]";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, t, 10);
+ check(r == JSMN_SUCCESS);
+ check(t[0].type == JSMN_ARRAY && t[0].start == 0 && t[0].end == 7);
+ check(t[1].type == JSMN_OBJECT && t[1].start == 1 && t[1].end == 3);
+ check(t[2].type == JSMN_OBJECT && t[2].start == 4 && t[2].end == 6);
+ return 0;
+}
+
+int test_simple() {
+ const char *js;
+ int r;
+ jsmn_parser p;
+ jsmntok_t tokens[10];
+
+ js = "{\"a\": 0}";
+
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tokens, 10);
+ check(r == JSMN_SUCCESS);
+ check(TOKEN_EQ(tokens[0], 0, 8, JSMN_OBJECT));
+ check(TOKEN_EQ(tokens[1], 2, 3, JSMN_STRING));
+ check(TOKEN_EQ(tokens[2], 6, 7, JSMN_PRIMITIVE));
+
+ check(TOKEN_STRING(js, tokens[0], js));
+ check(TOKEN_STRING(js, tokens[1], "a"));
+ check(TOKEN_STRING(js, tokens[2], "0"));
+
+ jsmn_init(&p);
+ js = "[\"a\":{},\"b\":{}]";
+ r = jsmn_parse(&p, js, tokens, 10);
+ check(r == JSMN_SUCCESS);
+
+ jsmn_init(&p);
+ js = "{\n \"Day\": 26,\n \"Month\": 9,\n \"Year\": 12\n }";
+ r = jsmn_parse(&p, js, tokens, 10);
+ check(r == JSMN_SUCCESS);
+
+ return 0;
+}
+
+int test_primitive() {
+ int r;
+ jsmn_parser p;
+ jsmntok_t tok[10];
+ const char *js;
+#ifndef JSMN_STRICT
+ js = "\"boolVar\" : true";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING
+ && tok[1].type == JSMN_PRIMITIVE);
+ check(TOKEN_STRING(js, tok[0], "boolVar"));
+ check(TOKEN_STRING(js, tok[1], "true"));
+
+ js = "\"boolVar\" : false";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING
+ && tok[1].type == JSMN_PRIMITIVE);
+ check(TOKEN_STRING(js, tok[0], "boolVar"));
+ check(TOKEN_STRING(js, tok[1], "false"));
+
+ js = "\"intVar\" : 12345";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING
+ && tok[1].type == JSMN_PRIMITIVE);
+ check(TOKEN_STRING(js, tok[0], "intVar"));
+ check(TOKEN_STRING(js, tok[1], "12345"));
+
+ js = "\"floatVar\" : 12.345";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING
+ && tok[1].type == JSMN_PRIMITIVE);
+ check(TOKEN_STRING(js, tok[0], "floatVar"));
+ check(TOKEN_STRING(js, tok[1], "12.345"));
+
+ js = "\"nullVar\" : null";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING
+ && tok[1].type == JSMN_PRIMITIVE);
+ check(TOKEN_STRING(js, tok[0], "nullVar"));
+ check(TOKEN_STRING(js, tok[1], "null"));
+#endif
+ return 0;
+}
+
+int test_string() {
+ int r;
+ jsmn_parser p;
+ jsmntok_t tok[10];
+ const char *js;
+
+ js = "\"strVar\" : \"hello world\"";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING
+ && tok[1].type == JSMN_STRING);
+ check(TOKEN_STRING(js, tok[0], "strVar"));
+ check(TOKEN_STRING(js, tok[1], "hello world"));
+
+ js = "\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\"";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING
+ && tok[1].type == JSMN_STRING);
+ check(TOKEN_STRING(js, tok[0], "strVar"));
+ check(TOKEN_STRING(js, tok[1], "escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\"));
+
+ js = "\"strVar\" : \"\"";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING
+ && tok[1].type == JSMN_STRING);
+ check(TOKEN_STRING(js, tok[0], "strVar"));
+ check(TOKEN_STRING(js, tok[1], ""));
+
+ return 0;
+}
+
+int test_partial_string() {
+ int r;
+ jsmn_parser p;
+ jsmntok_t tok[10];
+ const char *js;
+
+ jsmn_init(&p);
+ js = "\"x\": \"va";
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_ERROR_PART && tok[0].type == JSMN_STRING);
+ check(TOKEN_STRING(js, tok[0], "x"));
+ check(p.toknext == 1);
+
+ js = "\"x\": \"valu";
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_ERROR_PART && tok[0].type == JSMN_STRING);
+ check(TOKEN_STRING(js, tok[0], "x"));
+ check(p.toknext == 1);
+
+ js = "\"x\": \"value\"";
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING
+ && tok[1].type == JSMN_STRING);
+ check(TOKEN_STRING(js, tok[0], "x"));
+ check(TOKEN_STRING(js, tok[1], "value"));
+
+ js = "\"x\": \"value\", \"y\": \"value y\"";
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING
+ && tok[1].type == JSMN_STRING && tok[2].type == JSMN_STRING
+ && tok[3].type == JSMN_STRING);
+ check(TOKEN_STRING(js, tok[0], "x"));
+ check(TOKEN_STRING(js, tok[1], "value"));
+ check(TOKEN_STRING(js, tok[2], "y"));
+ check(TOKEN_STRING(js, tok[3], "value y"));
+
+ return 0;
+}
+
+int test_unquoted_keys() {
+#ifndef JSMN_STRICT
+ int r;
+ jsmn_parser p;
+ jsmntok_t tok[10];
+ const char *js;
+
+ jsmn_init(&p);
+ js = "key1: \"value\"\nkey2 : 123";
+
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_SUCCESS && tok[0].type == JSMN_PRIMITIVE
+ && tok[1].type == JSMN_STRING && tok[2].type == JSMN_PRIMITIVE
+ && tok[3].type == JSMN_PRIMITIVE);
+ check(TOKEN_STRING(js, tok[0], "key1"));
+ check(TOKEN_STRING(js, tok[1], "value"));
+ check(TOKEN_STRING(js, tok[2], "key2"));
+ check(TOKEN_STRING(js, tok[3], "123"));
+#endif
+ return 0;
+}
+
+int test_partial_array() {
+ int r;
+ jsmn_parser p;
+ jsmntok_t tok[10];
+ const char *js;
+
+ jsmn_init(&p);
+ js = " [ 1, true, ";
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_ERROR_PART && tok[0].type == JSMN_ARRAY
+ && tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE);
+
+ js = " [ 1, true, [123, \"hello";
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_ERROR_PART && tok[0].type == JSMN_ARRAY
+ && tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE
+ && tok[3].type == JSMN_ARRAY && tok[4].type == JSMN_PRIMITIVE);
+
+ js = " [ 1, true, [123, \"hello\"]";
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_ERROR_PART && tok[0].type == JSMN_ARRAY
+ && tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE
+ && tok[3].type == JSMN_ARRAY && tok[4].type == JSMN_PRIMITIVE
+ && tok[5].type == JSMN_STRING);
+ /* check child nodes of the 2nd array */
+ check(tok[3].size == 2);
+
+ js = " [ 1, true, [123, \"hello\"]]";
+ r = jsmn_parse(&p, js, tok, 10);
+ check(r == JSMN_SUCCESS && tok[0].type == JSMN_ARRAY
+ && tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE
+ && tok[3].type == JSMN_ARRAY && tok[4].type == JSMN_PRIMITIVE
+ && tok[5].type == JSMN_STRING);
+ check(tok[3].size == 2);
+ check(tok[0].size == 3);
+ return 0;
+}
+
+int test_array_nomem() {
+ int i;
+ int r;
+ jsmn_parser p;
+ jsmntok_t toksmall[10], toklarge[10];
+ const char *js;
+
+ js = " [ 1, true, [123, \"hello\"]]";
+
+ for (i = 0; i < 6; i++) {
+ jsmn_init(&p);
+ memset(toksmall, 0, sizeof(toksmall));
+ memset(toklarge, 0, sizeof(toklarge));
+ r = jsmn_parse(&p, js, toksmall, i);
+ check(r == JSMN_ERROR_NOMEM);
+
+ memcpy(toklarge, toksmall, sizeof(toksmall));
+
+ r = jsmn_parse(&p, js, toklarge, 10);
+ check(r == JSMN_SUCCESS);
+
+ check(toklarge[0].type == JSMN_ARRAY && toklarge[0].size == 3);
+ check(toklarge[3].type == JSMN_ARRAY && toklarge[3].size == 2);
+ }
+ return 0;
+}
+
+int test_objects_arrays() {
+ int i;
+ int r;
+ jsmn_parser p;
+ jsmntok_t tokens[10];
+ const char *js;
+
+ js = "[10}";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tokens, 10);
+ check(r == JSMN_ERROR_INVAL);
+
+ js = "[10]";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tokens, 10);
+ check(r == JSMN_SUCCESS);
+
+ js = "{\"a\": 1]";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tokens, 10);
+ check(r == JSMN_ERROR_INVAL);
+
+ js = "{\"a\": 1}";
+ jsmn_init(&p);
+ r = jsmn_parse(&p, js, tokens, 10);
+ check(r == JSMN_SUCCESS);
+
+ return 0;
+}
+
+int main() {
+ test(test_empty, "general test for a empty JSON objects/arrays");
+ test(test_simple, "general test for a simple JSON string");
+ test(test_primitive, "test primitive JSON data types");
+ test(test_string, "test string JSON data types");
+ test(test_partial_string, "test partial JSON string parsing");
+ test(test_partial_array, "test partial array reading");
+ test(test_array_nomem, "test array reading with a smaller number of tokens");
+ test(test_unquoted_keys, "test unquoted keys (like in JavaScript)");
+ test(test_objects_arrays, "test objects and arrays");
+ printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed);
+ return 0;
+}
+
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index 6e34e07..04604fd 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -30,6 +30,7 @@ const std::string Interpreter::getUUID() {
Interpreter::Interpreter() : Arabica::SAX2DOM::Parser<std::string>() {
_lastRunOnMainThread = 0;
+ _nsURL = "*";
_thread = NULL;
_sendQueue = NULL;
_parentQueue = NULL;
@@ -75,6 +76,14 @@ Interpreter* Interpreter::fromURI(const std::string& uri) {
return interpreter;
}
+void Interpreter::setName(const std::string& name) {
+ if (!_running) {
+ _name = name;
+ } else {
+ LOG(ERROR) << "Cannot change name of running interpreter";
+ }
+}
+
URL Interpreter::toBaseURI(const URL& uri) {
std::stringstream ssBaseURI;
if (uri.scheme().size() > 0) {
@@ -108,8 +117,21 @@ bool Interpreter::toAbsoluteURI(URL& uri) {
}
void Interpreter::startPrefixMapping(const std::string& prefix, const std::string& uri) {
-// std::cout << "starting prefix mapping " << prefix << ": " << uri << std::endl;
- _nsContext.addNamespaceDeclaration(uri, prefix);
+ std::cout << "starting prefix mapping " << prefix << ": " << uri << std::endl;
+ if (boost::iequals(uri, "http://www.w3.org/2005/07/scxml")) {
+ _nsURL = uri;
+ if (prefix.size() == 0) {
+ LOG(INFO) << "Mapped default namespace to 'scxml:'";
+ _xpathPrefix = "scxml:";
+ _nsContext.addNamespaceDeclaration(uri, "scxml");
+ } else {
+ _xpathPrefix = prefix + ":";
+ _xmlNSPrefix = _xpathPrefix;
+ _nsContext.addNamespaceDeclaration(uri, prefix);
+ }
+ } else {
+ _nsContext.addNamespaceDeclaration(uri, prefix);
+ }
}
Interpreter* Interpreter::fromInputSource(Arabica::SAX::InputSource<std::string>& source) {
@@ -140,22 +162,19 @@ void Interpreter::init() {
_sendQueue = new DelayedEventQueue();
_sendQueue->start();
if (_document) {
- NodeList<std::string> scxmls = _document.getElementsByTagName("scxml");
+ NodeList<std::string> scxmls = _document.getElementsByTagNameNS(_nsURL, "scxml");
if (scxmls.getLength() > 0) {
_scxml = (Arabica::DOM::Element<std::string>)scxmls.item(0);
- // do we have a xmlns attribute?
- std::string ns = _document.getDocumentElement().getNamespaceURI();
- if(ns.size() > 0) {
- _nsContext.addNamespaceDeclaration(ns, "sc");
- _nsPrefix = "sc:";
- }
- // do we have additional namespaces?
-
+ // setup xpath and check that it works
_xpath.setNamespaceContext(_nsContext);
+ Arabica::XPath::NodeSet<std::string> scxmls = _xpath.evaluate("/" + _xpathPrefix + "scxml", _document).asNodeSet();
+ assert(scxmls.size() > 0);
+ assert(scxmls[0] == _scxml);
normalize(_document);
- _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : getUUID());
+ if (_name.length() == 0)
+ _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : getUUID());
} else {
LOG(ERROR) << "Cannot find SCXML element" << std::endl;
}
@@ -216,11 +235,6 @@ bool Interpreter::runOnMainThread(int fps, bool blocking) {
return (_thread != NULL);
}
-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)
@@ -229,13 +243,15 @@ void Interpreter::interpret() {
_sessionId = getUUID();
- if(HAS_ATTR(_scxml, "datamodel")) {
- _dataModel = Factory::createDataModel(ATTR(_scxml, "datamodel"), this);
- if(!_dataModel) {
- LOG(ERROR) << "No datamodel for " << ATTR(_scxml, "datamodel") << " registered";
- return;
- }
- }
+ std::string datamodelName;
+ if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel"))
+ datamodelName = ATTR(_scxml, "datamodel");
+ if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "profile")) // SCION SCXML uses profile to specify datamodel
+ datamodelName = ATTR(_scxml, "profile");
+ _dataModel = Factory::createDataModel(datamodelName, this);
+ if(datamodelName.length() > 0 && !_dataModel) {
+ LOG(ERROR) << "No datamodel for " << datamodelName << " registered";
+ }
setupIOProcessors();
@@ -246,21 +262,21 @@ void Interpreter::interpret() {
if (_dataModel && _binding == EARLY) {
// initialize all data elements
- NodeSet<std::string> dataElems = _xpath.evaluate("//" + _nsPrefix + "data", _document).asNodeSet();
+ NodeSet<std::string> dataElems = _xpath.evaluate("//" + _xpathPrefix + "data", _document).asNodeSet();
for (unsigned int i = 0; i < dataElems.size(); i++) {
initializeData(dataElems[i]);
}
} else if(_dataModel) {
// initialize current data elements
- NodeSet<std::string> topDataElems = filterChildElements("data", filterChildElements("datamodel", _scxml));
- // NodeSet<std::string> topDataElems = _xpath.evaluate("/" + _nsPrefix + "scxml/" + _nsPrefix + "datamodel/" + _nsPrefix + "data", _document).asNodeSet();
+ NodeSet<std::string> topDataElems = filterChildElements(_xmlNSPrefix + "data", filterChildElements(_xmlNSPrefix + "datamodel", _scxml));
+ // NodeSet<std::string> topDataElems = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "datamodel/" + _xpathPrefix + "data", _document).asNodeSet();
for (unsigned int i = 0; i < topDataElems.size(); i++) {
initializeData(topDataElems[i]);
}
}
// executeGlobalScriptElements
- NodeSet<std::string> globalScriptElems = _xpath.evaluate("/" + _nsPrefix + "scxml/" + _nsPrefix + "script", _document).asNodeSet();
+ NodeSet<std::string> globalScriptElems = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "script", _document).asNodeSet();
for (unsigned int i = 0; i < globalScriptElems.size(); i++) {
// std::cout << globalScriptElems[i].getFirstChild().getNodeValue() << std::endl;
if (_dataModel)
@@ -268,12 +284,12 @@ void Interpreter::interpret() {
}
// initial transition might be implict
- NodeSet<std::string> initialTransitions = _xpath.evaluate("/" + _nsPrefix + "scxml/" + _nsPrefix + "initial/" + _nsPrefix + "transition", _document).asNodeSet();
+ NodeSet<std::string> initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _document).asNodeSet();
if (initialTransitions.size() == 0) {
Arabica::DOM::Element<std::string> initialState = (Arabica::DOM::Element<std::string>)getInitialState();
- Arabica::DOM::Element<std::string> initialElem = _document.createElement("initial");
- initialElem.setAttribute("generated", "on");
- Arabica::DOM::Element<std::string> transitionElem = _document.createElement("transition");
+ Arabica::DOM::Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial");
+ initialElem.setAttribute("generated", "true");
+ Arabica::DOM::Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition");
transitionElem.setAttribute("target", initialState.getAttribute("id"));
initialElem.appendChild(transitionElem);
_scxml.appendChild(initialElem);
@@ -285,6 +301,11 @@ void Interpreter::interpret() {
enterStates(initialTransitions);
mainEventLoop();
+
+ // set datamodel to null from this thread
+ if(_dataModel)
+ _dataModel = DataModel();
+
}
/**
@@ -333,7 +354,7 @@ void Interpreter::initializeData(const Arabica::DOM::Node<std::string>& data) {
void Interpreter::normalize(const Arabica::DOM::Document<std::string>& node) {
// make sure every state has an id and set isFirstEntry to true
- Arabica::XPath::NodeSet<std::string> states = _xpath.evaluate("//" + _nsPrefix + "state", _document).asNodeSet();
+ Arabica::XPath::NodeSet<std::string> states = _xpath.evaluate("//" + _xpathPrefix + "state", _document).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");
@@ -343,21 +364,21 @@ void Interpreter::normalize(const Arabica::DOM::Document<std::string>& node) {
}
// make sure every invoke has an idlocation or id
- Arabica::XPath::NodeSet<std::string> invokes = _xpath.evaluate("//" + _nsPrefix + "invoke", _document).asNodeSet();
+ Arabica::XPath::NodeSet<std::string> invokes = _xpath.evaluate("//" + _xpathPrefix + "invoke", _document).asNodeSet();
for (int i = 0; i < invokes.size(); i++) {
Arabica::DOM::Element<std::string> invokeElem = Arabica::DOM::Element<std::string>(invokes[i]);
if (!invokeElem.hasAttribute("id") && !invokeElem.hasAttribute("idlocation")) {
invokeElem.setAttribute("id", getUUID());
}
// // make sure every finalize element contained has the invoke id as an attribute
-// Arabica::XPath::NodeSet<std::string> finalizes = _xpath.evaluate("" + _nsPrefix + "finalize", invokeElem).asNodeSet();
+// Arabica::XPath::NodeSet<std::string> finalizes = _xpath.evaluate("" + _xpathPrefix + "finalize", invokeElem).asNodeSet();
// for (int j = 0; j < finalizes.size(); j++) {
// Arabica::DOM::Element<std::string> finalizeElem = Arabica::DOM::Element<std::string>(finalizes[j]);
// finalizeElem.setAttribute("invokeid", invokeElem.getAttribute("id"));
// }
}
- Arabica::XPath::NodeSet<std::string> finals = _xpath.evaluate("//" + _nsPrefix + "final", _document).asNodeSet();
+ Arabica::XPath::NodeSet<std::string> finals = _xpath.evaluate("//" + _xpathPrefix + "final", _document).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");
@@ -366,7 +387,7 @@ void Interpreter::normalize(const Arabica::DOM::Document<std::string>& node) {
}
}
- Arabica::XPath::NodeSet<std::string> histories = _xpath.evaluate("//" + _nsPrefix + "history", _document).asNodeSet();
+ Arabica::XPath::NodeSet<std::string> histories = _xpath.evaluate("//" + _xpathPrefix + "history", _document).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")) {
@@ -374,7 +395,7 @@ void Interpreter::normalize(const Arabica::DOM::Document<std::string>& node) {
}
}
- Arabica::XPath::NodeSet<std::string> scxml = _xpath.evaluate("/" + _nsPrefix + "scxml", _document).asNodeSet();
+ Arabica::XPath::NodeSet<std::string> scxml = _xpath.evaluate("/" + _xpathPrefix + "scxml", _document).asNodeSet();
if (!((Arabica::DOM::Element<std::string>)scxml[0]).hasAttribute("id")) {
((Arabica::DOM::Element<std::string>)scxml[0]).setAttribute("id", getUUID());
}
@@ -392,6 +413,8 @@ void Interpreter::normalize(const Arabica::DOM::Document<std::string>& node) {
}
void Interpreter::mainEventLoop() {
+ std::set<InterpreterMonitor*>::iterator monIter;
+
while(_running) {
NodeSet<std::string> enabledTransitions;
_stable = false;
@@ -406,6 +429,12 @@ void Interpreter::mainEventLoop() {
}
std::cout << std::endl;
#endif
+ std::set<InterpreterMonitor*>::iterator monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ (*monIter)->beforeMicroStep(this);
+ monIter++;
+ }
+
enabledTransitions = selectEventlessTransitions();
if (enabledTransitions.size() == 0) {
if (_internalQueue.size() == 0) {
@@ -426,7 +455,7 @@ void Interpreter::mainEventLoop() {
}
for (unsigned int i = 0; i < _statesToInvoke.size(); i++) {
- NodeSet<std::string> invokes = _xpath.evaluate("" + _nsPrefix + "invoke", _statesToInvoke[i]).asNodeSet();
+ NodeSet<std::string> invokes = _xpath.evaluate("" + _xpathPrefix + "invoke", _statesToInvoke[i]).asNodeSet();
for (unsigned int j = 0; j < invokes.size(); j++) {
invoke(invokes[j]);
}
@@ -436,11 +465,12 @@ void Interpreter::mainEventLoop() {
if (!_internalQueue.empty())
continue;
- {
- tthread::lock_guard<tthread::mutex> lock(_mutex);
- _stabilized.notify_all();
- }
-
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ (*monIter)->onStableConfiguration(this);
+ monIter++;
+ }
+
// whenever we have a stable configuration, run the mainThread hooks with 200fps
while(_externalQueue.isEmpty() && _thread == NULL) {
runOnMainThread(200);
@@ -461,14 +491,14 @@ void Interpreter::mainEventLoop() {
LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl;
}
for (unsigned int i = 0; i < _configuration.size(); i++) {
- NodeSet<std::string> invokes = _xpath.evaluate("" + _nsPrefix + "invoke", _configuration[i]).asNodeSet();
+ NodeSet<std::string> invokes = _xpath.evaluate("" + _xpathPrefix + "invoke", _configuration[i]).asNodeSet();
for (unsigned int j = 0; j < invokes.size(); j++) {
Arabica::DOM::Element<std::string> invokeElem = (Arabica::DOM::Element<std::string>)invokes[j];
std::string invokeId = invokeElem.getAttribute("id");
std::string autoForward = invokeElem.getAttribute("autoforward");
if (boost::iequals(invokeId, externalEvent.invokeid)) {
- Arabica::XPath::NodeSet<std::string> finalizes = _xpath.evaluate("" + _nsPrefix + "finalize", invokeElem).asNodeSet();
+ Arabica::XPath::NodeSet<std::string> finalizes = _xpath.evaluate("" + _xpathPrefix + "finalize", invokeElem).asNodeSet();
for (int k = 0; k < finalizes.size(); k++) {
Arabica::DOM::Element<std::string> finalizeElem = Arabica::DOM::Element<std::string>(finalizes[k]);
executeContent(finalizeElem);
@@ -486,7 +516,20 @@ void Interpreter::mainEventLoop() {
if (!enabledTransitions.empty())
microstep(enabledTransitions);
}
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ (*monIter)->beforeCompletion(this);
+ monIter++;
+ }
+
exitInterpreter();
+
+ monIter = _monitors.begin();
+ while(monIter != _monitors.end()) {
+ (*monIter)->afterCompletion(this);
+ monIter++;
+ }
+
}
void Interpreter::internalDoneSend(const Arabica::DOM::Node<std::string>& state) {
@@ -497,7 +540,7 @@ void Interpreter::internalDoneSend(const Arabica::DOM::Node<std::string>& state)
Arabica::DOM::Element<std::string> parent = (Arabica::DOM::Element<std::string>)stateElem.getParentNode();
Event event;
- Arabica::XPath::NodeSet<std::string> doneDatas = _xpath.evaluate("" + _nsPrefix + "donedata", stateElem).asNodeSet();
+ Arabica::XPath::NodeSet<std::string> doneDatas = _xpath.evaluate("" + _xpathPrefix + "donedata", stateElem).asNodeSet();
if (doneDatas.size() > 0) {
// only process first donedata element
Arabica::DOM::Node<std::string> doneData = doneDatas[0];
@@ -505,7 +548,7 @@ void Interpreter::internalDoneSend(const Arabica::DOM::Node<std::string>& state)
for (int i = 0; i < doneChilds.getLength(); i++) {
if (!doneChilds.item(i).getNodeType() == Node_base::ELEMENT_NODE)
continue;
- if (boost::iequals(TAGNAME(doneChilds.item(i)), "param")) {
+ if (boost::iequals(TAGNAME(doneChilds.item(i)), _xmlNSPrefix + "param")) {
if (!HAS_ATTR(doneChilds.item(i), "name")) {
LOG(ERROR) << "param element is missing name attribut";
continue;
@@ -522,7 +565,7 @@ void Interpreter::internalDoneSend(const Arabica::DOM::Node<std::string>& state)
}
event.compound[ATTR(doneChilds.item(i), "name")] = paramValue;
}
- if (boost::iequals(TAGNAME(doneChilds.item(i)), "content")) {
+ if (boost::iequals(TAGNAME(doneChilds.item(i)), _xmlNSPrefix + "content")) {
if (HAS_ATTR(doneChilds.item(i), "expr")) {
if (_dataModel) {
event.compound["content"] = Data(_dataModel.evalAsString(ATTR(doneChilds.item(i), "expr")), Data::VERBATIM);
@@ -625,7 +668,7 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
}
// params
- NodeSet<std::string> params = _xpath.evaluate("" + _nsPrefix + "param", element).asNodeSet();
+ NodeSet<std::string> params = _xpath.evaluate("" + _xpathPrefix + "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";
@@ -644,7 +687,7 @@ void Interpreter::send(const Arabica::DOM::Node<std::string>& element) {
}
// content
- NodeSet<std::string> contents = _xpath.evaluate("" + _nsPrefix + "content", element).asNodeSet();
+ NodeSet<std::string> contents = _xpath.evaluate("" + _xpathPrefix + "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) {
@@ -762,7 +805,7 @@ void Interpreter::invoke(const Arabica::DOM::Node<std::string>& element) {
}
// params
- NodeSet<std::string> params = _xpath.evaluate("" + _nsPrefix + "param", element).asNodeSet();
+ NodeSet<std::string> params = _xpath.evaluate("" + _xpathPrefix + "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";
@@ -786,7 +829,7 @@ void Interpreter::invoke(const Arabica::DOM::Node<std::string>& element) {
}
// content
- NodeSet<std::string> contents = _xpath.evaluate("" + _nsPrefix + "content", element).asNodeSet();
+ NodeSet<std::string> contents = _xpath.evaluate("" + _xpathPrefix + "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) {
@@ -843,7 +886,7 @@ Arabica::XPath::NodeSet<std::string> Interpreter::selectTransitions(const std::s
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();
+ NodeSet<std::string> transitions = _xpath.evaluate("" + _xpathPrefix + "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) &&
@@ -924,7 +967,7 @@ Arabica::XPath::NodeSet<std::string> Interpreter::selectEventlessTransitions() {
}
#if 0
- NodeSet<std::string> transitions = _xpath.evaluate("" + _nsPrefix + "transition", ancestors[j]).asNodeSet();
+ NodeSet<std::string> transitions = _xpath.evaluate("" + _xpathPrefix + "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]);
@@ -969,6 +1012,8 @@ LOOP:
}
bool Interpreter::isPreemptingTransition(const Arabica::DOM::Node<std::string>& t1, const Arabica::DOM::Node<std::string>& t2) {
+ assert(t1);
+ assert(t2);
if (t1 == t2)
return false;
if (isWithinSameChild(t1) && (!isTargetless(t2) && !isWithinSameChild(t2)))
@@ -1002,11 +1047,11 @@ void Interpreter::exitInterpreter() {
statesToExit.reverse();
for (int i = 0; i < statesToExit.size(); i++) {
- Arabica::XPath::NodeSet<std::string> onExitElems = _xpath.evaluate("" + _nsPrefix + "onexit", statesToExit[i]).asNodeSet();
+ Arabica::XPath::NodeSet<std::string> onExitElems = _xpath.evaluate("" + _xpathPrefix + "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();
+ Arabica::XPath::NodeSet<std::string> invokeElems = _xpath.evaluate("" + _xpathPrefix + "invoke", statesToExit[i]).asNodeSet();
for (int j = 0; j < invokeElems.size(); j++) {
cancelInvoke(invokeElems[j]);
}
@@ -1036,14 +1081,14 @@ void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content)
return;
if (false) {
- } else if (boost::iequals(TAGNAME(content), "raise")) {
+ } else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "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")) {
+ } else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "if")) {
// --- IF / ELSEIF / ELSE --------------
Arabica::DOM::Element<std::string> ifElem = (Arabica::DOM::Element<std::string>)content;
if(hasConditionMatch(ifElem)) {
@@ -1053,8 +1098,8 @@ void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content)
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"))
+ if (boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "elsif") ||
+ boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "else"))
break;
executeContent(childs.item(i));
}
@@ -1062,25 +1107,25 @@ void Interpreter::executeContent(const Arabica::DOM::Node<std::string>& content)
} else {
// condition does not match - do we have an elsif?
if (ifElem.hasChildNodes()) {
- NodeList<std::string> elseifElem = ifElem.getElementsByTagName("elseif");
+ NodeList<std::string> elseifElem = ifElem.getElementsByTagNameNS(_nsURL, "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");
+ NodeList<std::string> elseElem = ifElem.getElementsByTagNameNS(_nsURL, "else");
if (elseElem.getLength() > 0)
executeContent(elseElem.item(0).getChildNodes());
}
}
ELSIF_ELEM_MATCH:
;
- } else if (boost::iequals(TAGNAME(content), "elseif")) {
+ } else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "elseif")) {
std::cerr << "Found single elsif to evaluate!" << std::endl;
- } else if (boost::iequals(TAGNAME(content), "else")) {
+ } else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "else")) {
std::cerr << "Found single else to evaluate!" << std::endl;
- } else if (boost::iequals(TAGNAME(content), "foreach")) {
+ } else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "foreach")) {
// --- FOREACH --------------------------
if (_dataModel) {
if (HAS_ATTR(content, "array") && HAS_ATTR(content, "item")) {
@@ -1110,7 +1155,7 @@ ELSIF_ELEM_MATCH:
LOG(ERROR) << "Expected array and item attributes with foreach element!" << std::endl;
}
}
- } else if (boost::iequals(TAGNAME(content), "log")) {
+ } else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "log")) {
// --- LOG --------------------------
Arabica::DOM::Element<std::string> logElem = (Arabica::DOM::Element<std::string>)content;
if (logElem.hasAttribute("expr")) {
@@ -1124,7 +1169,7 @@ ELSIF_ELEM_MATCH:
std::cout << logElem.getAttribute("expr") << std::endl;
}
}
- } else if (boost::iequals(TAGNAME(content), "assign")) {
+ } else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "assign")) {
// --- ASSIGN --------------------------
if (_dataModel && HAS_ATTR(content, "location") && HAS_ATTR(content, "expr")) {
try {
@@ -1133,14 +1178,14 @@ ELSIF_ELEM_MATCH:
LOG(ERROR) << "Syntax error in attributes of assign element:" << std::endl << e << std::endl;
}
}
- } else if (boost::iequals(TAGNAME(content), "validate")) {
+ } else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "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")) {
+ } else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "script")) {
// --- SCRIPT --------------------------
if (_dataModel) {
if (HAS_ATTR(content, "src")) {
@@ -1171,10 +1216,10 @@ ELSIF_ELEM_MATCH:
}
}
}
- } else if (boost::iequals(TAGNAME(content), "send")) {
+ } else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "send")) {
// --- SEND --------------------------
send(content);
- } else if (boost::iequals(TAGNAME(content), "cancel")) {
+ } else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "cancel")) {
// --- CANCEL --------------------------
std::string sendId;
try {
@@ -1192,7 +1237,7 @@ ELSIF_ELEM_MATCH:
LOG(ERROR) << "Syntax error while executing cancel element" << std::endl << e << std::endl;
}
- } else if (boost::iequals(TAGNAME(content), "invoke")) {
+ } else if (boost::iequals(TAGNAME(content), _xmlNSPrefix + "invoke")) {
// --- INVOKE --------------------------
} else {
NodeList<std::string> executable = content.getChildNodes();
@@ -1393,7 +1438,7 @@ void Interpreter::enterStates(const Arabica::XPath::NodeSet<std::string>& enable
}
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();
+ Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", stateElem).asNodeSet();
for (int j = 0; j < transitions.size(); j++) {
executeContent(transitions[j]);
}
@@ -1432,7 +1477,7 @@ void Interpreter::enterStates(const Arabica::XPath::NodeSet<std::string>& enable
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"))
+ if (boost::iequals(TAGNAME(parentElem), _xmlNSPrefix + "scxml"))
return true;
return false;
}
@@ -1479,7 +1524,7 @@ void Interpreter::addStatesToEnter(const Arabica::DOM::Node<std::string>& state,
}
}
} else {
- NodeSet<std::string> transitions = _xpath.evaluate("" + _nsPrefix + "transition", state).asNodeSet();
+ NodeSet<std::string> transitions = _xpath.evaluate("" + _xpathPrefix + "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++) {
@@ -1524,9 +1569,9 @@ Arabica::XPath::NodeSet<std::string> Interpreter::getChildStates(const Arabica::
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 << ATTR(states[i], "id") << " - " << TAGNAME(states[i]) << ", ";
// }
-// std::cout << std::flush;
+// std::cout << std::endl << 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?
@@ -1543,7 +1588,7 @@ NEXT_ANCESTOR:
;
}
assert(ancestor);
-// std::cout << " -> " << ((Arabica::DOM::Element<std::string>)ancestor).getAttribute("id") << " " << ancestor.getLocalName() << std::endl;
+// std::cout << " -> " << ATTR(ancestor, "id") << " " << ancestor.getLocalName() << std::endl;
return ancestor;
}
@@ -1554,17 +1599,17 @@ 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 + "']", _document).asNodeSet();
+ NodeSet<std::string> target = _xpath.evaluate("//" + _xpathPrefix + "state[@id='" + stateId + "']", _document).asNodeSet();
if (target.size() > 0)
goto FOUND;
// now parallel states
- target = _xpath.evaluate("//" + _nsPrefix + "parallel[@id='" + stateId + "']", _document).asNodeSet();
+ target = _xpath.evaluate("//" + _xpathPrefix + "parallel[@id='" + stateId + "']", _document).asNodeSet();
if (target.size() > 0)
goto FOUND;
// now final states
- target = _xpath.evaluate("//" + _nsPrefix + "final[@id='" + stateId + "']", _document).asNodeSet();
+ target = _xpath.evaluate("//" + _xpathPrefix + "final[@id='" + stateId + "']", _document).asNodeSet();
if (target.size() > 0)
goto FOUND;
@@ -1579,7 +1624,7 @@ FOUND:
}
Arabica::DOM::Node<std::string> Interpreter::getSourceState(const Arabica::DOM::Node<std::string>& transition) {
- if (boost::iequals(TAGNAME(transition.getParentNode()), "initial"))
+ if (boost::iequals(TAGNAME(transition.getParentNode()), _xmlNSPrefix + "initial"))
return transition.getParentNode().getParentNode();
return transition.getParentNode();
}
@@ -1594,7 +1639,7 @@ Arabica::DOM::Node<std::string> Interpreter::getSourceState(const Arabica::DOM::
Arabica::DOM::Node<std::string> Interpreter::getInitialState(Arabica::DOM::Node<std::string> state) {
if (!state) {
state = _document.getFirstChild();
- while(!isState(state))
+ while(state && !isState(state))
state = state.getNextSibling();
}
@@ -1607,8 +1652,8 @@ Arabica::DOM::Node<std::string> Interpreter::getInitialState(Arabica::DOM::Node<
}
// initial element as child - but not the implicit generated one
- NodeSet<std::string> initialStates = _xpath.evaluate("" + _nsPrefix + "initial", state).asNodeSet();
- if(initialStates.size() == 1 && !boost::iequals(ATTR(initialStates[0], "generated"), "on"))
+ NodeSet<std::string> initialStates = _xpath.evaluate("" + _xpathPrefix + "initial", state).asNodeSet();
+ if(initialStates.size() == 1 && !boost::iequals(ATTR(initialStates[0], "generated"), "true"))
return initialStates[0];
// first child state
@@ -1628,7 +1673,7 @@ NodeSet<std::string> Interpreter::getTargetStates(const Arabica::DOM::Node<std::
if (isState(transition)) {
NodeList<std::string> childs = transition.getChildNodes();
for (int i = 0; i < childs.getLength(); i++) {
- if (childs.item(i).getNodeType() == Node_base::ELEMENT_NODE && boost::iequals(TAGNAME(childs.item(i)), "transition")) {
+ if (childs.item(i).getNodeType() == Node_base::ELEMENT_NODE && boost::iequals(TAGNAME(childs.item(i)), _xmlNSPrefix + "transition")) {
targetStates.push_back(getTargetStates(childs.item(i)));
}
}
@@ -1673,7 +1718,7 @@ NodeSet<std::string> Interpreter::filterChildElements(const std::string& tagName
NodeList<std::string> childs = node.getChildNodes();
for (unsigned int i = 0; i < childs.getLength(); i++) {
if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE ||
- !boost::iequals(TAGNAME(childs.item(i)), tagName))
+ !boost::iequals(LOCALNAME(childs.item(i)), tagName))
continue;
filteredChildElems.push_back(childs.item(i));
}
@@ -1688,7 +1733,7 @@ NodeSet<std::string> Interpreter::getProperAncestors(const Arabica::DOM::Node<st
while((node = node.getParentNode())) {
if (!isState(node))
break;
- if (!boost::iequals(TAGNAME(node), "parallel") && !boost::iequals(TAGNAME(node), "state") && !boost::iequals(TAGNAME(node), "scxml"))
+ if (!boost::iequals(TAGNAME(node), _xmlNSPrefix + "parallel") && !boost::iequals(TAGNAME(node), _xmlNSPrefix + "state") && !boost::iequals(TAGNAME(node), _xmlNSPrefix + "scxml"))
break;
if (node == s2)
break;
@@ -1720,9 +1765,9 @@ bool Interpreter::isTargetless(const Arabica::DOM::Node<std::string>& transition
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;
+ assert(transition.getParentNode());
// @todo: do we need to look at parallel as well?
- if (xpath.evaluate("" + _nsPrefix + "state[id=\"" + target + "\"]", transition.getParentNode()).asNodeSet().size() > 0)
+ if (_xpath.evaluate("" + _xpathPrefix + "state[id=\"" + target + "\"]", transition.getParentNode()).asNodeSet().size() > 0)
return true;
}
return false;
@@ -1734,7 +1779,7 @@ bool Interpreter::isState(const Arabica::DOM::Node<std::string>& state) {
if (state.getNodeType() != Arabica::DOM::Node_base::ELEMENT_NODE)
return false;
- std::string tagName = TAGNAME(state);
+ std::string tagName = LOCALNAME(state);
if (boost::iequals("state", tagName))
return true;
if (boost::iequals("scxml", tagName))
@@ -1747,7 +1792,7 @@ bool Interpreter::isState(const Arabica::DOM::Node<std::string>& state) {
}
bool Interpreter::isFinal(const Arabica::DOM::Node<std::string>& state) {
- std::string tagName = TAGNAME(state);
+ std::string tagName = LOCALNAME(state);
if (boost::iequals("final", tagName))
return true;
if (HAS_ATTR(state, "final") && boost::iequals("true", ATTR(state, "final")))
@@ -1770,7 +1815,7 @@ bool Interpreter::isInitial(const Arabica::DOM::Node<std::string>& state) {
}
bool Interpreter::isPseudoState(const Arabica::DOM::Node<std::string>& state) {
- std::string tagName = TAGNAME(state);
+ std::string tagName = LOCALNAME(state);
if (boost::iequals("initial", tagName))
return true;
if (boost::iequals("history", tagName))
@@ -1779,15 +1824,15 @@ bool Interpreter::isPseudoState(const Arabica::DOM::Node<std::string>& state) {
}
bool Interpreter::isTransitionTarget(const Arabica::DOM::Node<std::string>& elem) {
- return (isState(elem) || boost::iequals(TAGNAME(elem), "history"));
+ return (isState(elem) || boost::iequals(LOCALNAME(elem), "history"));
}
bool Interpreter::isAtomic(const Arabica::DOM::Node<std::string>& state) {
- if (boost::iequals("final", TAGNAME(state)))
+ if (boost::iequals("final", LOCALNAME(state)))
return true;
// I will assume that parallel states are not meant to be atomic.
- if (boost::iequals("parallel", TAGNAME(state)))
+ if (boost::iequals("parallel", LOCALNAME(state)))
return false;
Arabica::DOM::NodeList<std::string> childs = state.getChildNodes();
@@ -1799,7 +1844,7 @@ bool Interpreter::isAtomic(const Arabica::DOM::Node<std::string>& state) {
}
bool Interpreter::isHistory(const Arabica::DOM::Node<std::string>& state) {
- if (boost::iequals("history", TAGNAME(state)))
+ if (boost::iequals("history", LOCALNAME(state)))
return true;
return false;
}
@@ -1807,7 +1852,7 @@ bool Interpreter::isHistory(const Arabica::DOM::Node<std::string>& state) {
bool Interpreter::isParallel(const Arabica::DOM::Node<std::string>& state) {
if (!isState(state))
return false;
- if (boost::iequals("parallel", TAGNAME(state)))
+ if (boost::iequals("parallel", LOCALNAME(state)))
return true;
return false;
}
@@ -1817,7 +1862,7 @@ bool Interpreter::isCompound(const Arabica::DOM::Node<std::string>& state) {
if (!isState(state))
return false;
- if (boost::iequals(TAGNAME(state), "parallel"))
+ if (boost::iequals(LOCALNAME(state), "parallel"))
return false;
Arabica::DOM::NodeList<std::string> childs = state.getChildNodes();
@@ -1858,244 +1903,11 @@ IOProcessor Interpreter::getIOProcessor(const std::string& type) {
return _ioProcessors[type];
}
-//IOProcessor* Interpreter::getIOProcessorForId(const std::string& sendId) {
-// if (_sendQueue.find(sendId) == _sendQueue.end()) {
-// LOG(ERROR) << "No ioProcessor with pending send id " << sendId << sendId;
-// return NULL;
-// }
-// return _ioProcessorsIds[sendId];
-//}
-
-bool Interpreter::validate() {
- bool validationErrors = false;
-
- if (!_document) {
- LOG(ERROR) << "Document " << _baseURI.asString() << " was not parsed successfully" << std::endl;
- return false;
- }
-
- // semantic issues ------------
- if ((_xpath.evaluate("/" + _nsPrefix + "scxml/@datamodel", _document).asNodeSet().size() == 0) &&
- _xpath.evaluate("/" + _nsPrefix + "scxml/" + _nsPrefix + "script", _document).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", _document).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", _document).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", _document).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", _document).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", _document).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", _document).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", _document).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", _document).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", _document).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 (!_document)
return;
- dump(_document);
+ std::cout << _document;
}
-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
index 61d09e0..54c7a78 100644
--- a/src/uscxml/Interpreter.h
+++ b/src/uscxml/Interpreter.h
@@ -26,6 +26,14 @@
namespace uscxml {
+class InterpreterMonitor {
+public:
+ virtual void onStableConfiguration(Interpreter* interpreter) {}
+ virtual void beforeCompletion(Interpreter* interpreter) {}
+ virtual void afterCompletion(Interpreter* interpreter) {}
+ virtual void beforeMicroStep(Interpreter* interpreter) {}
+};
+
class NumAttr {
public:
NumAttr(const std::string& str) {
@@ -74,7 +82,14 @@ public:
};
void interpret();
- bool validate();
+
+ void addMonitor(InterpreterMonitor* monitor) {
+ _monitors.insert(monitor);
+ }
+
+ void removeMonitor(InterpreterMonitor* monitor) {
+ _monitors.erase(monitor);
+ }
void setBaseURI(std::string baseURI) {
_baseURI = URL(baseURI);
@@ -84,21 +99,22 @@ public:
}
bool toAbsoluteURI(URL& uri);
- DataModel getDataModel() {
+ DataModel getDataModel() {
return _dataModel;
}
void setParentQueue(uscxml::concurrency::BlockingQueue<Event>* parentQueue) {
_parentQueue = parentQueue;
}
- std::string getNSPrefix() {
- return _nsPrefix;
+ std::string getXPathPrefix() {
+ return _xpathPrefix;
+ }
+ std::string getXMLPrefix() {
+ return _xmlNSPrefix;
}
Arabica::XPath::StandardNamespaceContext<std::string>& getNSContext() {
return _nsContext;
}
- void waitForStabilization();
-
void receive(Event& event) {
_externalQueue.push(event);
}
@@ -113,6 +129,7 @@ public:
return _document;
}
+ void setName(const std::string& name);
const std::string& getName() {
return _name;
}
@@ -125,7 +142,6 @@ public:
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);
static bool isState(const Arabica::DOM::Node<std::string>& state);
static bool isPseudoState(const Arabica::DOM::Node<std::string>& state);
@@ -159,14 +175,15 @@ protected:
bool _stable;
tthread::thread* _thread;
tthread::mutex _mutex;
- tthread::condition_variable _stabilized;
URL _baseURI;
Arabica::DOM::Document<std::string> _document;
Arabica::DOM::Element<std::string> _scxml;
Arabica::XPath::XPath<std::string> _xpath;
Arabica::XPath::StandardNamespaceContext<std::string> _nsContext;
- std::string _nsPrefix;
+ std::string _xmlNSPrefix; // the actual prefix for elements in the xml file
+ std::string _xpathPrefix; // prefix mapped for xpath, "scxml" is _xmlNSPrefix is empty but _nsURL set
+ std::string _nsURL; // ough to be "http://www.w3.org/2005/07/scxml"
bool _running;
bool _done;
@@ -182,6 +199,8 @@ protected:
uscxml::concurrency::BlockingQueue<Event>* _parentQueue;
DelayedEventQueue* _sendQueue;
+ std::set<InterpreterMonitor*> _monitors;
+
static URL toBaseURI(const URL& url);
void microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions);
@@ -201,7 +220,7 @@ protected:
Arabica::XPath::NodeSet<std::string> selectTransitions(const std::string& event);
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);
+ Arabica::XPath::NodeSet<std::string> getProperAncestors(const Arabica::DOM::Node<std::string>& s1, const Arabica::DOM::Node<std::string>& s2);
void send(const Arabica::DOM::Node<std::string>& element);
diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp
index 09676bf..8a3ee1e 100644
--- a/src/uscxml/Message.cpp
+++ b/src/uscxml/Message.cpp
@@ -4,6 +4,10 @@
#include <DOM/SAX2DOM/SAX2DOM.hpp>
#include <SAX/helpers/CatchErrorHandler.hpp>
+extern "C" {
+#include "jsmn.h" // minimal json parser
+}
+
namespace uscxml {
static int _dataIndentation = 1;
@@ -167,7 +171,70 @@ Data Data::fromXML(const std::string& xmlString) {
return Data();
}
-Event Event::fromXML(const std::string& xmlString) {
+Data Data::fromJSON(const std::string& jsonString) {
+ Data data;
+
+ // unimplemented
+// assert(false);
+
+ jsmn_parser p;
+ jsmntok_t t[1024];
+ memset(&t, 0, sizeof(t));
+ jsmn_init(&p);
+
+ int rv = jsmn_parse(&p, jsonString.c_str(), t, 1024);
+ if (rv != 0) {
+ return data;
+ }
+
+ std::list<Data*> dataStack;
+ std::list<jsmntok_t> tokenStack;
+ dataStack.push_back(&data);
+
+ size_t currTok = 0;
+ do {
+ switch (t[currTok].type) {
+ case JSMN_STRING:
+ dataStack.back()->type = Data::VERBATIM;
+ case JSMN_PRIMITIVE:
+ dataStack.back()->atom = jsonString.substr(t[currTok].start, t[currTok].end - t[currTok].start);
+ dataStack.pop_back();
+ currTok++;
+ break;
+ case JSMN_OBJECT:
+ case JSMN_ARRAY:
+ tokenStack.push_back(t[currTok]);
+ currTok++;
+ break;
+ }
+
+ // there are no more tokens
+ if (t[currTok].end == 0 || tokenStack.empty())
+ break;
+
+// std::cout << "Token Stack: [" << tokenStack.back().start << ", " << tokenStack.back().end << "]" << std::endl;
+// std::cout << "Token Curr: [" << t[currTok].start << ", " << t[currTok].end << "]" << std::endl;
+
+ // next token starts after current one => pop
+ if (t[currTok].end > tokenStack.back().end)
+ tokenStack.pop_back();
+
+ if (tokenStack.back().type == JSMN_OBJECT && (t[currTok].type == JSMN_PRIMITIVE || t[currTok].type == JSMN_STRING)) {
+ // grab key and push new data
+ dataStack.push_back(&(dataStack.back()->compound[jsonString.substr(t[currTok].start, t[currTok].end - t[currTok].start)]));
+ currTok++;
+ }
+ if (tokenStack.back().type == JSMN_ARRAY) {
+ // push new index
+ dataStack.back()->array.push_back(Data());
+ dataStack.push_back(&(dataStack.back()->array.back()));
+ }
+
+ } while (true);
+ return data;
+}
+
+ Event Event::fromXML(const std::string& xmlString) {
Arabica::SAX2DOM::Parser<std::string> eventParser;
Arabica::SAX::CatchErrorHandler<std::string> errorHandler;
eventParser.setErrorHandler(errorHandler);
@@ -258,31 +325,36 @@ std::ostream& operator<< (std::ostream& os, const Data& data) {
for (unsigned int i = 0; i < longestKey; i++)
keyPadding += " ";
- os << "{" << std::endl;
+ std::string seperator;
+ os << std::endl << indent << "{";
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;
+ os << seperator << std::endl << indent << " \"" << compoundIter->first << "\": " << keyPadding.substr(0, longestKey - compoundIter->first.size());
+ _dataIndentation += 1;
+ os << compoundIter->second;
+ _dataIndentation -= 1;
+ seperator = ", ";
compoundIter++;
}
- os << indent << "}" << std::endl;
+ os << std::endl << indent << "}";
} 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++;
+
+ std::string seperator;
+ os << std::endl << indent << "[";
+ std::list<Data>::const_iterator arrayIter = data.array.begin();
+ while(arrayIter != data.array.end()) {
+ _dataIndentation += 1;
+ os << seperator << *arrayIter;
+ _dataIndentation -= 1;
+ seperator = ", ";
+ arrayIter++;
}
- os << indent << "]" << std::endl;
+ os << std::endl << indent << "]";
} else if (data.atom.size() > 0) {
if (data.type == Data::VERBATIM) {
- os << indent << "\"" << data.atom << "\"";
+ os << "\"" << data.atom << "\"";
} else {
- os << indent << data.atom;
+ os << data.atom;
}
}
return os;
diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h
index a006ff6..68e8151 100644
--- a/src/uscxml/Message.h
+++ b/src/uscxml/Message.h
@@ -30,6 +30,7 @@ public:
Data(const Arabica::DOM::Node<std::string>& dom);
virtual ~Data() {}
+ static Data fromJSON(const std::string& jsonString);
static Data fromXML(const std::string& xmlString);
Arabica::DOM::Document<std::string> toDocument();
std::string toXMLString() {
diff --git a/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp
index b051310..3ed39d6 100644
--- a/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp
+++ b/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.cpp
@@ -112,7 +112,7 @@ void EventIOProcessor::send(const SendRequest& req) {
_httpConnections[endPoint] = evhttp_connection_base_new(_asyncQueue._eventLoop, _dns, evhttp_uri_get_host(targetURI), evhttp_uri_get_port(targetURI));
struct evhttp_connection* httpConn = _httpConnections[endPoint];
- struct evhttp_request* httpReq = evhttp_request_new(EventIOProcessor::httpSendReqDone, this);
+ struct evhttp_request* httpReq = evhttp_request_new(EventIOServer::httpSendReqDoneCallback, this);
// event name
if (req.name.size() > 0) {
@@ -163,7 +163,7 @@ void EventIOProcessor::send(const SendRequest& req) {
}
}
-void EventIOProcessor::httpRecvReq(struct evhttp_request *req, void *arg) {
+void EventIOProcessor::httpRecvReq(struct evhttp_request *req) {
const char *cmdtype;
struct evkeyvalq *headers;
@@ -242,13 +242,11 @@ void EventIOProcessor::httpRecvReq(struct evhttp_request *req, void *arg) {
reqEvent.compound["content"] = Data(content, Data::VERBATIM);
}
- EventIOProcessor* INSTANCE = (EventIOProcessor*)arg;
- INSTANCE->returnEvent(reqEvent);
-
+ returnEvent(reqEvent);
evhttp_send_reply(req, 200, "OK", NULL);
}
-void EventIOProcessor::httpSendReqDone(struct evhttp_request *req, void *cb_arg) {
+void EventIOProcessor::httpSendReqDone(struct evhttp_request *req) {
if (req) {
LOG(INFO) << "got return code " << evhttp_request_get_response_code(req) << std::endl;
}
@@ -294,7 +292,7 @@ void EventIOServer::registerProcessor(EventIOProcessor* processor) {
* If the interpreter does not specify a name, take its sessionid.
*/
- std::string path = processor->_interpreter->getName();
+ std::string path = processor->getPath();
if (path.size() == 0) {
path = processor->_interpreter->getSessionId();
}
@@ -316,7 +314,8 @@ void EventIOServer::registerProcessor(EventIOProcessor* processor) {
LOG(INFO) << "SCXML listening at: " << processorURL.str() << std::endl;
- evhttp_set_cb(INSTANCE->_http, ("/" + actualPath.str()).c_str(), EventIOProcessor::httpRecvReq, processor);
+ evhttp_set_cb(INSTANCE->_http, ("/" + actualPath.str()).c_str(), EventIOServer::httpRecvReqCallback, processor);
+
// evhttp_set_cb(THIS->_http, "/", EventIOProcessor::httpRecvReq, processor);
// evhttp_set_gencb(THIS->_http, EventIOProcessor::httpRecvReq, NULL);
}
@@ -334,6 +333,7 @@ void EventIOServer::start() {
void EventIOServer::run(void* instance) {
EventIOServer* INSTANCE = (EventIOServer*)instance;
+ LOG(INFO) << "HTTP Server started" << std::endl;
while(INSTANCE->_isRunning) {
event_base_dispatch(INSTANCE->_base);
}
diff --git a/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h b/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h
index 3afe463..783332e 100644
--- a/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h
+++ b/src/uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h
@@ -47,9 +47,9 @@ public:
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);
+ virtual std::string getPath() { return _interpreter->getName(); }
+ virtual void httpSendReqDone(struct evhttp_request *req);
+ virtual void httpRecvReq(struct evhttp_request *req);
protected:
std::map<std::string, SendData> _sendData;
@@ -65,8 +65,13 @@ protected:
};
class EventIOServer {
+public:
+ static EventIOServer* getInstance();
+
+ static void registerProcessor(EventIOProcessor* processor);
+ static void unregisterProcessor(EventIOProcessor* processor);
+
private:
- static EventIOServer* getInstance();
EventIOServer(unsigned short port);
~EventIOServer();
@@ -77,9 +82,13 @@ private:
void determineAddress();
static std::string syncResolve(const std::string& hostname);
- static void registerProcessor(EventIOProcessor* processor);
- static void unregisterProcessor(EventIOProcessor* processor);
+ static void httpSendReqDoneCallback(struct evhttp_request *req, void *cb_arg) {
+ ((EventIOProcessor*)cb_arg)->httpSendReqDone(req);
+ }
+ static void httpRecvReqCallback(struct evhttp_request *req, void *cb_arg) {
+ ((EventIOProcessor*)cb_arg)->httpRecvReq(req);
+ }
std::map<std::string, EventIOProcessor*> _processors;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 5e5e697..88d10e9 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,3 +1,9 @@
+find_program(XMLLINT xmllint)
+if (XMLLINT)
+ file(GLOB SCXML_FILES samples/uscxml/*.scxml)
+
+endif()
+
add_executable(test-predicates src/test-predicates.cpp)
target_link_libraries(test-predicates uscxml)
add_test(test-predicates ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-predicates ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-predicates.scxml)
@@ -8,17 +14,17 @@ target_link_libraries(test-execution uscxml)
add_test(test-execution ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-execution ${CMAKE_SOURCE_DIR}/test/samples/uscxml/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_RUNTIME_OUTPUT_DIRECTORY}/test-apache-commons ${CMAKE_SOURCE_DIR}/test/samples/apache)
-set_target_properties(test-apache-commons 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_RUNTIME_OUTPUT_DIRECTORY}/test-apache-commons ${CMAKE_SOURCE_DIR}/test/samples/apache)
+# set_target_properties(test-apache-commons PROPERTIES FOLDER "Tests")
-if (V8_FOUND)
- add_executable(test-ecmascript-v8 src/test-ecmascript-v8.cpp)
- target_link_libraries(test-ecmascript-v8 uscxml)
- add_test(test-ecmascript-v8 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-ecmascript-v8 ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-ecmascript.scxml)
- set_target_properties(test-ecmascript-v8 PROPERTIES FOLDER "Tests")
-endif()
+# if (V8_FOUND)
+# add_executable(test-ecmascript-v8 src/test-ecmascript-v8.cpp)
+# target_link_libraries(test-ecmascript-v8 uscxml)
+# add_test(test-ecmascript-v8 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-ecmascript-v8 ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-ecmascript.scxml)
+# set_target_properties(test-ecmascript-v8 PROPERTIES FOLDER "Tests")
+# endif()
if (SWI_FOUND)
add_executable(test-prolog-swi src/test-prolog-swi.cpp)
@@ -59,3 +65,10 @@ add_executable(test-url src/test-url.cpp)
target_link_libraries(test-url uscxml)
add_test(test-url ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-url)
set_target_properties(test-url PROPERTIES FOLDER "Tests")
+
+add_executable(scxml-test-framework-client
+ ${PROJECT_SOURCE_DIR}/contrib/src/jsmn/jsmn.c
+ src/scxml-test-framework-client.cpp)
+target_link_libraries(scxml-test-framework-client uscxml)
+set_target_properties(scxml-test-framework-client PROPERTIES FOLDER "Tests")
+
diff --git a/test/run-scxml-test-framework.sh b/test/run-scxml-test-framework.sh
new file mode 100755
index 0000000..34f804d
--- /dev/null
+++ b/test/run-scxml-test-framework.sh
@@ -0,0 +1,166 @@
+#!/bin/sh
+
+set -e
+
+ME=`basename $0`
+DIR="$( cd "$( dirname "$0" )" && pwd )"
+
+if [ -z $1 ]; then
+ echo
+ echo "Expected filename of scxml-test-framework-client as first argument"
+ echo
+ exit;
+fi
+SCXML_TEST_FRAMEWORK_FULL="$( cd "$(dirname "$1")" && pwd)/$(basename $1)"
+SCXML_TEST_FRAMEWORK_NAME=$(basename $1)
+
+if [[ ! -x "${SCXML_TEST_FRAMEWORK_FULL}" ]]; then
+ echo
+ echo "${SCXML_TEST_FRAMEWORK_FULL} not an executable file"
+ echo
+fi
+
+TESTS=""
+# TESTS="${TESTS} scxml-test-framework/test/actionSend/send1.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/actionSend/send2.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/actionSend/send3.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/actionSend/send4.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/actionSend/send5.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/actionSend/send6.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/actionSend/send7.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/actionSend/send8.scxml" # failed
+
+# TESTS="${TESTS} scxml-test-framework/test/assign-current-small-step/test0.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/assign-current-small-step/test1.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/assign-current-small-step/test2.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/assign-current-small-step/test3.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/assign-current-small-step/test4.scxml" # passed
+
+# TESTS="${TESTS} scxml-test-framework/test/assign-next-small-step/test0.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/assign-next-small-step/test1.scxml" # never terminates: getData not defined
+# TESTS="${TESTS} scxml-test-framework/test/assign-next-small-step/test2.scxml" # never terminates: getData not defined
+# TESTS="${TESTS} scxml-test-framework/test/assign-next-small-step/test3.scxml" # failed
+
+# TESTS="${TESTS} scxml-test-framework/test/atom3-basic-tests/m0.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/atom3-basic-tests/m1.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/atom3-basic-tests/m2.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/atom3-basic-tests/m3.scxml" # failed
+
+# TESTS="${TESTS} scxml-test-framework/test/basic/basic0.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/basic/basic1.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/basic/basic2.scxml" # passed
+
+# TESTS="${TESTS} scxml-test-framework/test/cond-js/test0.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/cond-js/test1.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/cond-js/test2.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/cond-js/TestConditionalTransition.scxml" # failed
+
+# TESTS="${TESTS} scxml-test-framework/test/default-initial-state/initial1.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/default-initial-state/initial2.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/default-initial-state/initial3.scxml" # passed
+
+# TESTS="${TESTS} scxml-test-framework/test/delayedSend/send1.scxml" # segfault
+# TESTS="${TESTS} scxml-test-framework/test/delayedSend/send2.scxml" # segfault
+# TESTS="${TESTS} scxml-test-framework/test/delayedSend/send3.scxml" # segfault
+
+# TESTS="${TESTS} scxml-test-framework/test/documentOrder/documentOrder0.scxml" # passed
+
+# TESTS="${TESTS} scxml-test-framework/test/foreach/test1.scxml" # passed
+
+# TESTS="${TESTS} scxml-test-framework/test/hierarchy/hier0.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/hierarchy/hier1.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/hierarchy/hier2.scxml" # failed
+
+# TESTS="${TESTS} scxml-test-framework/test/hierarchy+documentOrder/test0.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/hierarchy+documentOrder/test1.scxml" # failed
+
+# TESTS="${TESTS} scxml-test-framework/test/history/history0.scxml" # segfault
+# TESTS="${TESTS} scxml-test-framework/test/history/history1.scxml" # segfault
+# TESTS="${TESTS} scxml-test-framework/test/history/history2.scxml" # segfault
+# TESTS="${TESTS} scxml-test-framework/test/history/history3.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/history/history4.scxml" # segfault
+# TESTS="${TESTS} scxml-test-framework/test/history/history5.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/history/history6.scxml" # segfault
+
+# TESTS="${TESTS} scxml-test-framework/test/if-else/test0.scxml" # failed
+
+# TESTS="${TESTS} scxml-test-framework/test/in/TestInPredicate.scxml" # failed
+
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test0.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test1.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test2.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test3.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test4.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test5.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test6.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test7.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test8.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/more-parallel/test9.scxml" # failed
+
+# TESTS="${TESTS} scxml-test-framework/test/multiple-events-per-transition/test1.scxml" # passed
+
+# TESTS="${TESTS} scxml-test-framework/test/parallel/test0.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel/test1.scxml" # exception
+# TESTS="${TESTS} scxml-test-framework/test/parallel/test2.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel/test3.scxml" # failed
+
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test0.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test1.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test10.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test11.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test12.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test13.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test14.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test15.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test16.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test17.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test18.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test19.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test2.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test20.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test21.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test22.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test23.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test24.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test25.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test26.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test27.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test28.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test29.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test3.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test30.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test31.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test4.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test5.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test6.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test7.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test8.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/parallel+interrupt/test9.scxml" # failed
+
+# TESTS="${TESTS} scxml-test-framework/test/script/test0.scxml" # getData not defined
+# TESTS="${TESTS} scxml-test-framework/test/script/test1.scxml" # getData not defined
+# TESTS="${TESTS} scxml-test-framework/test/script/test2.scxml" # getData not defined
+# TESTS="${TESTS} scxml-test-framework/test/script-src/test0.scxml" # getData not defined
+# TESTS="${TESTS} scxml-test-framework/test/script-src/test1.scxml" # getData not defined
+# TESTS="${TESTS} scxml-test-framework/test/script-src/test2.scxml" # getData not defined
+# TESTS="${TESTS} scxml-test-framework/test/script-src/test3.scxml" # getData not defined
+
+# TESTS="${TESTS} scxml-test-framework/test/scxml-prefix-event-name-matching/star0.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/scxml-prefix-event-name-matching/test0.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/scxml-prefix-event-name-matching/test1.scxml" # passed
+
+# TESTS="${TESTS} scxml-test-framework/test/send-data/send1.scxml" # segfault
+# TESTS="${TESTS} scxml-test-framework/test/send-internal/test0.scxml" # failed
+
+# TESTS="${TESTS} scxml-test-framework/test/targetless-transition/test0.scxml" # passed
+# TESTS="${TESTS} scxml-test-framework/test/targetless-transition/test1.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/targetless-transition/test2.scxml" # failed
+# TESTS="${TESTS} scxml-test-framework/test/targetless-transition/test3.scxml" # failed
+
+
+trap 'killall ${SCXML_TEST_FRAMEWORK_NAME}' 0
+$SCXML_TEST_FRAMEWORK_FULL &
+sleep 1
+cd $DIR
+
+node scxml-test-framework --test-server-url http://localhost:8080/test $TESTS
diff --git a/test/samples/uscxml/test-execution.scxml b/test/samples/uscxml/test-execution.scxml
index ada1a17..13373ba 100644
--- a/test/samples/uscxml/test-execution.scxml
+++ b/test/samples/uscxml/test-execution.scxml
@@ -1,4 +1,8 @@
-<scxml initial="step2">
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+ initial="step2">
<state id="start">
<onentry>
<log expr="'Entered State: start'" />
diff --git a/test/schema/scxml-attribs.xsd b/test/schema/scxml-attribs.xsd
new file mode 100644
index 0000000..98aff4c
--- /dev/null
+++ b/test/schema/scxml-attribs.xsd
@@ -0,0 +1,57 @@
+<?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 common attributes for SCXML
+ </xsd:documentation>
+ <xsd:documentation source="scxml-copyright.xsd"/>
+ </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 module itself does not provide the schemaLocation
+ and expects the driver schema to provide the
+ actual SchemaLocation.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:import>
+ <xsd:include schemaLocation="scxml-datatypes.xsd">
+ <xsd:annotation>
+ <xsd:documentation>
+ This include brings in the SCXML datatypes.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:include>
+
+ <xsd:attributeGroup name="Fetchtimeout.attrib">
+ <xsd:annotation>
+ <xsd:documentation>Used in Cache.attribs</xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="fetchtimeout" type="Duration.datatype"/>
+ </xsd:attributeGroup>
+ <xsd:attributeGroup name="Maxage.attrib">
+ <xsd:annotation>
+ <xsd:documentation>Used in Cache.attribs</xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="maxage" type="Integer.datatype"/>
+ </xsd:attributeGroup>
+ <xsd:attributeGroup name="Maxstale.attrib">
+ <xsd:annotation>
+ <xsd:documentation>Used in Cache attribs</xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="maxstale" type="Integer.datatype"/>
+ </xsd:attributeGroup>
+
+ <xsd:attributeGroup name="Cache.attribs">
+ <xsd:annotation>
+ <xsd:documentation>Cache attributes to control caching behavior</xsd:documentation>
+ </xsd:annotation>
+ <xsd:attributeGroup ref="Fetchtimeout.attrib"/>
+ <xsd:attributeGroup ref="Maxage.attrib"/>
+ <xsd:attributeGroup ref="Maxstale.attrib"/>
+ </xsd:attributeGroup>
+</xsd:schema>
diff --git a/test/schema/scxml-contentmodels.xsd b/test/schema/scxml-contentmodels.xsd
new file mode 100644
index 0000000..2850c3a
--- /dev/null
+++ b/test/schema/scxml-contentmodels.xsd
@@ -0,0 +1,35 @@
+<?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>
+ XML Schema content models for SCXML
+ * scxml.extra.content
+ * content
+ * scxml.extra.attribs
+ Defines SCXML shared content models.
+ </xsd:documentation>
+ <xsd:documentation source="scxml-copyright.xsd"/>
+ </xsd:annotation>
+
+ <xsd:attributeGroup name="scxml.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:group name="scxml.extra.content">
+ <xsd:annotation>
+ <xsd:documentation>
+ group allowing elements from other namespaces
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+
+</xsd:schema>
diff --git a/test/schema/scxml-datatypes.xsd b/test/schema/scxml-datatypes.xsd
new file mode 100644
index 0000000..7771084
--- /dev/null
+++ b/test/schema/scxml-datatypes.xsd
@@ -0,0 +1,203 @@
+<?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>
+ XML Schema datatypes for SCXML
+
+ Defines containers for the SCXML datatypes, many of these
+ imported from other specifications and standards.
+
+ </xsd:documentation>
+ <xsd:documentation source="scxml-copyright.xsd"/>
+ </xsd:annotation>
+
+ <xsd:simpleType name="Exmode.datatype">
+ <xsd:annotation>
+ <xsd:documentation>
+ Describes the processor execution mode for this document, being
+ either "lax" or "strict".
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="lax"/>
+ <xsd:enumeration value="strict"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="Binding.datatype">
+ <xsd:annotation>
+ <xsd:documentation>
+ The binding type in use for the SCXML document.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="early"/>
+ <xsd:enumeration value="late"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+
+ <xsd:simpleType name="HistoryType.datatype">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="shallow"/>
+ <xsd:enumeration value="deep"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="TransitionType.datatype">
+ <xsd:annotation>
+ <xsd:documentation>
+ The type of the transition i.e. internal or external.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="internal"/>
+ <xsd:enumeration value="external"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="Boolean.datatype">
+ <xsd:annotation>
+ <xsd:documentation>
+ Boolean: true or false only
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKENS">
+ <xsd:enumeration value="true"/>
+ <xsd:enumeration value="false"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="AssignType.datatype">
+ <xsd:annotation>
+ <xsd:documentation>
+ The assign type that allows for precise manipulation of the
+ datamodel location. Types are:
+ replacechildren (default),
+ firstchild, lastchild,
+ previoussibling, nextsibling,
+ replace, delete,
+ addattribute
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="replacechildren"/>
+ <xsd:enumeration value="firstchild"/>
+ <xsd:enumeration value="lastchild"/>
+ <xsd:enumeration value="previoussibling"/>
+ <xsd:enumeration value="nextsibling"/>
+ <xsd:enumeration value="replace"/>
+ <xsd:enumeration value="delete"/>
+ <xsd:enumeration value="addattribute"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="URI.datatype">
+ <xsd:annotation>
+ <xsd:documentation>
+ The xsd:anyURI type and thus URI references in SCXML
+ documents may contain a wide array of international
+ characters. Implementers should reference RFC 3987 and
+ the "Character Model for the World Wide Web 1.0:
+ Resource Identifiers" in order to provide appropriate
+ support for these characters in VoiceXML documents and
+ when processing values of this type or mapping them to
+ URIs.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:anyURI"/>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="Integer.datatype">
+ <xsd:annotation>
+ <xsd:documentation>Non-negative integer</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:nonNegativeInteger"/>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="Duration.datatype">
+ <xsd:annotation>
+ <xsd:documentation>
+ Duration allowing positive values ranging from milliseconds
+ to days.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="\d*(\.\d+)?(ms|s|m|h|d)"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+
+ <xsd:simpleType name="EventType.datatype">
+ <xsd:annotation>
+ <xsd:documentation>
+ EventType is the name of an event.
+ Example legal values:
+ foo
+ foo.bar
+ foo.bar.baz
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:pattern value="(\i|\d|\-)+(\.(\i|\d|\-)+)*"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="EventTypes.datatype">
+ <xsd:annotation>
+ <xsd:documentation>
+ Custom datatype for the event attribute in SCXML based on xsd:token.
+ Example legal values:
+ *
+ foo
+ foo.bar
+ foo.*
+ foo.bar.*
+ foo bar baz
+ foo.bar bar.* baz.foo.*
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:pattern value="\.?\*|(\i|\d|\-)+(\.(\i|\d|\-)+)*(\.\*)?(\s(\i|\d|\-)+(\.(\i|\d|\-)+)*(\.\*)?)*"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <!-- Defines the default CondLang datatype. -->
+ <xsd:simpleType name="CondLang.datatype">
+ <xsd:annotation>
+ <xsd:documentation>
+ Conditional language is expression
+ which must evaluate to Boolean True or False.
+ The expression language must define In(stateID)
+ as a valid expression.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string"/>
+ </xsd:simpleType>
+
+ <!-- Defines the default LocLang datatype. -->
+ <xsd:simpleType name="LocLang.datatype">
+ <xsd:annotation>
+ <xsd:documentation>
+ Location language is expression
+ identifying a location in the datamodel.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string"/>
+ </xsd:simpleType>
+
+ <!-- Defines the default ValueLang datatype. -->
+ <xsd:simpleType name="ValueLang.datatype">
+ <xsd:annotation>
+ <xsd:documentation>
+ Value language is expression
+ return a value.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string"/>
+ </xsd:simpleType>
+</xsd:schema>
diff --git a/test/schema/scxml-message.xsd b/test/schema/scxml-message.xsd
deleted file mode 100644
index de4b4b8..0000000
--- a/test/schema/scxml-message.xsd
+++ /dev/null
@@ -1,122 +0,0 @@
-<?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-module-core.xsd b/test/schema/scxml-module-core.xsd
new file mode 100644
index 0000000..5245bc9
--- /dev/null
+++ b/test/schema/scxml-module-core.xsd
@@ -0,0 +1,405 @@
+<?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 core module for SCXML
+ * scxml
+ * state
+ * initial
+ * onexit
+ * onentry
+ * transition
+ * parallel
+ * final
+ * history
+ * donedata
+ * if
+ * elsif
+ * else
+ * foreach
+ * raise
+ * log
+ The core module defines these elements and the
+ attributes.
+ </xsd:documentation>
+ <xsd:documentation source="scxml-copyright.xsd"/>
+ </xsd:annotation>
+
+ <xsd:include schemaLocation="scxml-datatypes.xsd">
+ <xsd:annotation>
+ <xsd:documentation>
+ Includes common SCXML datatypes
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:include>
+
+ <xsd:include schemaLocation="scxml-attribs.xsd">
+ <xsd:annotation>
+ <xsd:documentation>
+ Includes common SCXML attributes
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:include>
+
+ <xsd:include schemaLocation="scxml-contentmodels.xsd">
+ <xsd:annotation>
+ <xsd:documentation>
+ This module defines Common content model extensions for SCXML
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:include>
+
+
+ <!-- scxml -->
+ <xsd:attributeGroup name="scxml.scxml.attlist">
+ <xsd:attribute name="initial" type="xsd:IDREFS"/>
+ <xsd:attribute name="name" type="xsd:NMTOKEN"/>
+ <xsd:attribute name="version" type="xsd:decimal" use="required" fixed="1.0"/>
+ <xsd:attribute name="datamodel" type="xsd:NMTOKEN" default="null" use="optional"/>
+ <xsd:attribute name="binding" type="Binding.datatype"/>
+ <xsd:attribute name="exmode" type="Exmode.datatype"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.scxml.mix">
+ <xsd:choice>
+ <xsd:element ref="state" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="parallel" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="final" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="datamodel" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="script" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:choice>
+ </xsd:group>
+ <xsd:group name="scxml.scxml.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.scxml.mix" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.scxml.type">
+ <xsd:group ref="scxml.scxml.content"/>
+ <xsd:attributeGroup ref="scxml.scxml.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="scxml" type="scxml.scxml.type"/>
+
+ <!-- state -->
+ <xsd:attributeGroup name="scxml.state.attlist">
+ <xsd:attribute name="id" type="xsd:ID"/>
+ <xsd:attribute name="initial" type="xsd:IDREFS"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.state.mix">
+ <xsd:choice>
+ <xsd:element ref="onentry" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="onexit" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="transition" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="initial" minOccurs="0" maxOccurs="1"/>
+ <xsd:element ref="state" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="parallel" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="final" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="history" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="datamodel" minOccurs="0" maxOccurs="1"/>
+ <xsd:element ref="invoke" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:choice>
+ </xsd:group>
+ <xsd:group name="scxml.state.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.state.mix" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.state.type">
+ <xsd:sequence>
+ <xsd:group ref="scxml.state.content"/>
+ </xsd:sequence>
+ <xsd:attributeGroup ref="scxml.state.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="state" type="scxml.state.type"/>
+
+ <!-- initial -->
+ <xsd:attributeGroup name="scxml.initial.attlist">
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.initial.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="transition" minOccurs="1" maxOccurs="1"/>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.initial.type">
+ <xsd:group ref="scxml.initial.content"/>
+ <xsd:attributeGroup ref="scxml.initial.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="initial" type="scxml.initial.type"/>
+
+ <!-- onentry -->
+ <xsd:attributeGroup name="scxml.onentry.attlist">
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.onentry.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.core.executablecontent" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.onentry.type">
+ <xsd:group ref="scxml.onentry.content"/>
+ <xsd:attributeGroup ref="scxml.onentry.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="onentry" type="scxml.onentry.type"/>
+
+ <!-- onexit -->
+ <xsd:attributeGroup name="scxml.onexit.attlist">
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.onexit.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.core.executablecontent" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.onexit.type">
+ <xsd:group ref="scxml.onexit.content"/>
+ <xsd:attributeGroup ref="scxml.onexit.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="onexit" type="scxml.onexit.type"/>
+
+ <!-- transition -->
+ <xsd:attributeGroup name="scxml.transition.attlist">
+ <xsd:attribute name="event" type="EventTypes.datatype"/>
+ <xsd:attribute name="cond" type="CondLang.datatype"/>
+ <xsd:attribute name="target" type="xsd:IDREFS"/>
+ <xsd:attribute name="type" type="TransitionType.datatype"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.transition.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.core.executablecontent" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.transition.type">
+ <xsd:group ref="scxml.transition.content"/>
+ <xsd:attributeGroup ref="scxml.transition.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="transition" type="scxml.transition.type"/>
+
+ <!-- parallel -->
+ <xsd:attributeGroup name="scxml.parallel.attlist">
+ <xsd:attribute name="id" type="xsd:ID"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.parallel.mix">
+ <xsd:choice>
+ <xsd:element ref="onentry" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="onexit" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="transition" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="state" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="parallel" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="history" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="datamodel" minOccurs="0" maxOccurs="1"/>
+ <xsd:element ref="invoke" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:choice>
+ </xsd:group>
+ <xsd:group name="scxml.parallel.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.parallel.mix" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.parallel.type">
+ <xsd:group ref="scxml.parallel.content"/>
+ <xsd:attributeGroup ref="scxml.parallel.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="parallel" type="scxml.parallel.type"/>
+
+ <!-- final -->
+ <xsd:attributeGroup name="scxml.final.attlist">
+ <xsd:attribute name="id" type="xsd:ID"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.final.mix">
+ <xsd:choice>
+ <xsd:element ref="onentry" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="onexit" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="donedata" minOccurs="0" maxOccurs="1"/>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:choice>
+ </xsd:group>
+ <xsd:group name="scxml.final.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.final.mix" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.final.type">
+ <xsd:group ref="scxml.final.content"/>
+ <xsd:attributeGroup ref="scxml.final.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="final" type="scxml.final.type"/>
+
+ <!-- history -->
+ <xsd:attributeGroup name="scxml.history.attlist">
+ <xsd:attribute name="id" type="xsd:ID"/>
+ <xsd:attribute name="type" type="HistoryType.datatype"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.history.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="transition" minOccurs="1" maxOccurs="1"/>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.history.type">
+ <xsd:group ref="scxml.history.content"/>
+ <xsd:attributeGroup ref="scxml.history.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="history" type="scxml.history.type"/>
+
+
+
+ <!-- donedata -->
+ <xsd:attributeGroup name="scxml.donedata.attlist">
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.donedata.content">
+ <xsd:choice>
+ <xsd:element ref="content" minOccurs="0" maxOccurs="1"/>
+ <xsd:element ref="param" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:choice>
+ </xsd:group>
+ <xsd:complexType name="scxml.donedata.type">
+ <xsd:group ref="scxml.donedata.content"/>
+ <xsd:attributeGroup ref="scxml.donedata.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="donedata" type="scxml.donedata.type"/>
+
+ <!-- if -->
+ <xsd:attributeGroup name="scxml.if.attlist">
+ <xsd:attribute name="cond" type="CondLang.datatype" use="required"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.if.elseif.mix">
+ <xsd:sequence>
+ <xsd:element ref="elseif" />
+ <xsd:group ref="scxml.core.executablecontent" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:group name="scxml.if.else.mix">
+ <xsd:sequence>
+ <xsd:element ref="else" />
+ <xsd:group ref="scxml.core.executablecontent" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:group name="scxml.if.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.core.executablecontent" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:group ref="scxml.if.elseif.mix" minOccurs="0" maxOccurs="1"/>
+ <xsd:group ref="scxml.if.else.mix" minOccurs="0" maxOccurs="1"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.if.type">
+ <xsd:group ref="scxml.if.content"/>
+ <xsd:attributeGroup ref="scxml.if.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="if" type="scxml.if.type"/>
+
+ <!-- elseif -->
+ <xsd:attributeGroup name="scxml.elseif.attlist">
+ <xsd:attribute name="cond" type="CondLang.datatype" use="required"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.elseif.mix">
+ <xsd:choice>
+ <!-- No content for this element -->
+ </xsd:choice>
+ </xsd:group>
+ <xsd:group name="scxml.elseif.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.elseif.mix" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.elseif.type">
+ <xsd:group ref="scxml.elseif.content"/>
+ <xsd:attributeGroup ref="scxml.elseif.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="elseif" type="scxml.elseif.type"/>
+
+ <!-- else -->
+ <xsd:attributeGroup name="scxml.else.attlist">
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.else.mix">
+ <xsd:choice>
+ <!-- No content for this element -->
+ </xsd:choice>
+ </xsd:group>
+ <xsd:group name="scxml.else.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.else.mix" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.else.type">
+ <xsd:group ref="scxml.else.content"/>
+ <xsd:attributeGroup ref="scxml.else.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="else" type="scxml.else.type"/>
+
+ <!-- foreach -->
+ <xsd:attributeGroup name="scxml.foreach.attlist">
+ <xsd:attribute name="array" type="ValueLang.datatype" use="required"/>
+ <xsd:attribute name="item" type="xsd:string" use="required"/>
+ <xsd:attribute name="index" type="xsd:string"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.foreach.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.core.executablecontent" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.foreach.type">
+ <xsd:group ref="scxml.foreach.content"/>
+ <xsd:attributeGroup ref="scxml.foreach.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="foreach" type="scxml.foreach.type"/>
+
+ <!-- raise -->
+ <xsd:attributeGroup name="scxml.raise.attlist">
+ <xsd:attribute name="event" type="xsd:NMTOKEN" use="required"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.raise.mix">
+ <xsd:choice>
+ <!-- No content for this element -->
+ </xsd:choice>
+ </xsd:group>
+ <xsd:group name="scxml.raise.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.raise.mix" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.raise.type">
+ <xsd:group ref="scxml.raise.content"/>
+ <xsd:attributeGroup ref="scxml.raise.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="raise" type="scxml.raise.type"/>
+
+ <!-- log -->
+ <xsd:attributeGroup name="scxml.log.attlist">
+ <xsd:attribute name="label" type="xsd:string"/>
+ <xsd:attribute name="expr" type="ValueLang.datatype"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.log.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.log.type">
+ <xsd:group ref="scxml.log.content"/>
+ <xsd:attributeGroup ref="scxml.log.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="log" type="scxml.log.type"/>
+
+
+</xsd:schema>
diff --git a/test/schema/scxml-module-data.xsd b/test/schema/scxml-module-data.xsd
new file mode 100644
index 0000000..ec96e71
--- /dev/null
+++ b/test/schema/scxml-module-data.xsd
@@ -0,0 +1,151 @@
+<?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 data module for SCXML
+ * datamodel
+ * data
+ * assign
+ * param
+ * script
+ * content
+ The data module defines these elements and their
+ attributes.
+ </xsd:documentation>
+ <xsd:documentation source="scxml-copyright.xsd"/>
+ </xsd:annotation>
+
+ <xsd:include schemaLocation="scxml-datatypes.xsd">
+ <xsd:annotation>
+ <xsd:documentation>
+ This module defines SCXML Attribute DataTypes
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:include>
+
+ <xsd:include schemaLocation="scxml-attribs.xsd">
+ <xsd:annotation>
+ <xsd:documentation>
+ This module defines Common attributes for SCXML
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:include>
+
+ <xsd:include schemaLocation="scxml-contentmodels.xsd">
+ <xsd:annotation>
+ <xsd:documentation>
+ This module defines Common content model extensions for SCXML
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:include>
+
+ <!-- datamodel -->
+ <xsd:attributeGroup name="scxml.datamodel.attlist">
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.datamodel.content">
+ <xsd:sequence>
+ <xsd:element ref="data" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.datamodel.type">
+ <xsd:group ref="scxml.datamodel.content"/>
+ <xsd:attributeGroup ref="scxml.datamodel.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="datamodel" type="scxml.datamodel.type"/>
+
+ <!-- data -->
+ <xsd:attributeGroup name="scxml.data.attlist">
+ <xsd:attribute name="id" type="xsd:ID" use="required"/>
+ <xsd:attribute name="src" type="URI.datatype"/>
+ <xsd:attribute name="expr" type="ValueLang.datatype"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.data.content">
+ <xsd:sequence>
+ <xsd:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.data.type" mixed="true">
+ <xsd:group ref="scxml.data.content"/>
+ <xsd:attributeGroup ref="scxml.data.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="data" type="scxml.data.type"/>
+
+
+
+ <!-- param -->
+ <xsd:attributeGroup name="scxml.param.attlist">
+ <xsd:attribute name="name" type="xsd:NMTOKEN" use="required"/>
+ <xsd:attribute name="expr" type="ValueLang.datatype"/>
+ <xsd:attribute name="location" type="LocLang.datatype"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.param.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.param.type">
+ <xsd:group ref="scxml.param.content"/>
+ <xsd:attributeGroup ref="scxml.param.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="param" type="scxml.param.type"/>
+
+<!-- assign -->
+ <xsd:attributeGroup name="scxml.assign.attlist">
+ <xsd:attribute name="location" type="LocLang.datatype" use="required"/>
+ <xsd:attribute name="expr" type="ValueLang.datatype"/>
+ <xsd:attribute name="type" type="AssignType.datatype" default="replacechildren"/>
+ <xsd:attribute name="attr" type="xsd:NMTOKEN"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.assign.content">
+ <xsd:sequence>
+ <xsd:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.assign.type" mixed="true">
+ <xsd:group ref="scxml.assign.content"/>
+ <xsd:attributeGroup ref="scxml.assign.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="assign" type="scxml.assign.type"/>
+
+
+<!-- script -->
+ <xsd:attributeGroup name="scxml.script.attlist">
+ <xsd:attribute name="src" type="URI.datatype"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.script.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.script.type" mixed="true">
+ <xsd:group ref="scxml.script.content"/>
+ <xsd:attributeGroup ref="scxml.script.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="script" type="scxml.script.type"/>
+
+ <!-- content -->
+ <xsd:attributeGroup name="scxml.content.attlist">
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ <xsd:attribute name="expr" type="ValueLang.datatype"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.content.content">
+ <xsd:sequence>
+ <xsd:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.content.type" mixed="true">
+ <xsd:group ref="scxml.content.content"/>
+ <xsd:attributeGroup ref="scxml.content.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="content" type="scxml.content.type"/>
+
+</xsd:schema>
diff --git a/test/schema/scxml-module-external.xsd b/test/schema/scxml-module-external.xsd
new file mode 100644
index 0000000..ae6ced3
--- /dev/null
+++ b/test/schema/scxml-module-external.xsd
@@ -0,0 +1,152 @@
+<?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 external module for SCXML
+ * send
+ * cancel
+ * invoke
+ * finalize
+ The external module defines these elements and their
+ attributes.
+ </xsd:documentation>
+ <xsd:documentation source="scxml-copyright.xsd"/>
+ </xsd:annotation>
+
+ <xsd:include schemaLocation="scxml-datatypes.xsd">
+ <xsd:annotation>
+ <xsd:documentation>
+ This module defines SCXML Attribute DataTypes
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:include>
+
+ <xsd:include schemaLocation="scxml-attribs.xsd">
+ <xsd:annotation>
+ <xsd:documentation>
+ This module defines Common attributes for SCXML
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:include>
+
+ <xsd:include schemaLocation="scxml-contentmodels.xsd">
+ <xsd:annotation>
+ <xsd:documentation>
+ This module defines Common content model extensions for SCXML
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:include>
+
+<!-- send -->
+ <xsd:attributeGroup name="scxml.send.attlist">
+ <xsd:attribute name="event" type="EventType.datatype"/>
+ <xsd:attribute name="eventexpr" type="ValueLang.datatype"/>
+ <xsd:attribute name="target" type="URI.datatype"/>
+ <xsd:attribute name="targetexpr" type="ValueLang.datatype"/>
+ <xsd:attribute name="type" type="xsd:string" default="scxml"/>
+ <xsd:attribute name="typeexpr" type="ValueLang.datatype"/>
+ <xsd:attribute name="id" type="xsd:ID"/>
+ <xsd:attribute name="idlocation" type="LocLang.datatype"/>
+ <xsd:attribute name="delay" type="Duration.datatype" default="0s"/>
+ <xsd:attribute name="delayexpr" type="ValueLang.datatype"/>
+ <xsd:attribute name="namelist" type="xsd:string"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.send.mix">
+ <xsd:choice>
+ <xsd:element ref="content" minOccurs="0" maxOccurs="1"/>
+ <xsd:element ref="param" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:choice>
+ </xsd:group>
+ <xsd:group name="scxml.send.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.send.mix" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.send.type">
+ <xsd:group ref="scxml.send.content"/>
+ <xsd:attributeGroup ref="scxml.send.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="send" type="scxml.send.type"/>
+
+ <!-- cancel -->
+ <xsd:attributeGroup name="scxml.cancel.attlist">
+ <xsd:attribute name="sendid" type="xsd:IDREF"/>
+ <xsd:attribute name="sendidexpr" type="ValueLang.datatype"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.cancel.mix">
+ <xsd:sequence>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:group name="scxml.cancel.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.cancel.mix" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.cancel.type">
+ <xsd:group ref="scxml.cancel.content"/>
+ <xsd:attributeGroup ref="scxml.cancel.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="cancel" type="scxml.cancel.type"/>
+
+
+
+ <!-- invoke -->
+ <xsd:attributeGroup name="scxml.invoke.attlist">
+ <xsd:attribute name="type" type="xsd:string" default="scxml"/>
+ <xsd:attribute name="typeexpr" type="ValueLang.datatype"/>
+ <xsd:attribute name="src" type="URI.datatype"/>
+ <xsd:attribute name="srcexpr" type="ValueLang.datatype"/>
+ <xsd:attribute name="id" type="xsd:ID"/>
+ <xsd:attribute name="idlocation" type="LocLang.datatype"/>
+ <xsd:attribute name="namelist" type="xsd:string"/>
+ <xsd:attribute name="autoforward" type="Boolean.datatype" use="optional" default="false"/>
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.invoke.mix">
+ <xsd:sequence>
+ <xsd:element ref="content" minOccurs="0" maxOccurs="1"/>
+ <xsd:element ref="param" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="finalize" minOccurs="0" maxOccurs="1"/>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:group name="scxml.invoke.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.invoke.mix" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.invoke.type">
+ <xsd:group ref="scxml.invoke.content"/>
+ <xsd:attributeGroup ref="scxml.invoke.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="invoke" type="scxml.invoke.type"/>
+
+ <!-- finalize -->
+ <xsd:attributeGroup name="scxml.finalize.attlist">
+ <xsd:attributeGroup ref="scxml.extra.attribs"/>
+ </xsd:attributeGroup>
+ <xsd:group name="scxml.finalize.mix">
+ <xsd:sequence>
+ <xsd:group ref="scxml.core.executablecontent"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:group name="scxml.finalize.content">
+ <xsd:sequence>
+ <xsd:group ref="scxml.finalize.mix" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:group>
+ <xsd:complexType name="scxml.finalize.type">
+ <xsd:group ref="scxml.finalize.content"/>
+ <xsd:attributeGroup ref="scxml.finalize.attlist"/>
+ </xsd:complexType>
+ <xsd:element name="finalize" type="scxml.finalize.type"/>
+
+
+</xsd:schema>
diff --git a/test/schema/scxml.xsd b/test/schema/scxml.xsd
index ebc0654..000a909 100644
--- a/test/schema/scxml.xsd
+++ b/test/schema/scxml.xsd
@@ -45,132 +45,44 @@
</xsd:annotation>
</xsd:import>
- <xsd:include schemaLocation="scxml-attribs.xsd">
+<xsd:include schemaLocation="scxml-module-core.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>
+ This imports the core elements for SCXML.
+ </xsd:documentation>
</xsd:annotation>
</xsd:include>
-
- <xsd:redefine schemaLocation="scxml-module-data.xsd">
+
+ <xsd:include 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.
+ This imports the data modelelements for SCXML.
</xsd:documentation>
</xsd:annotation>
</xsd:include>
- <xsd:redefine schemaLocation="scxml-module-external.xsd">
+ <xsd:include 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
+ This imports the external communications elements for SCXML.
</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:include>
+
+<!-- the various elements of executable content are defined in the relevant modules.
+This gathers them up into a single type -->
+ <xsd:group name="scxml.core.executablecontent">
+ <xsd:choice>
+ <xsd:group ref="scxml.extra.content" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element ref="raise"/>
+ <xsd:element ref="if"/>
+ <xsd:element ref="foreach"/>
+ <xsd:element ref="send"/>
+ <xsd:element ref="script"/>
+ <xsd:element ref="assign"/>
+ <xsd:element ref="log"/>
+ <xsd:element ref="cancel"/>
+ </xsd:choice>
+ </xsd:group>
</xsd:schema>
diff --git a/test/src/scxml-test-framework-client.cpp b/test/src/scxml-test-framework-client.cpp
new file mode 100644
index 0000000..8f26362
--- /dev/null
+++ b/test/src/scxml-test-framework-client.cpp
@@ -0,0 +1,147 @@
+#include "uscxml/Interpreter.h"
+#include "uscxml/plugins/ioprocessor/basichttp/libevent/EventIOProcessor.h"
+#include <sstream>
+
+extern "C" {
+#include "jsmn.h" // minimal json parser
+}
+
+#include <event2/keyvalq_struct.h>
+#include <event2/buffer.h>
+
+/**
+ POST /foo HTTP/1.1
+ host: localhost:9000
+ accept: application/json
+ content-type: application/json
+ content-length: 92
+ Connection: keep-alive
+
+ {"load":"http://localhost:9999/scxml-test-framework/test/targetless-transition/test3.scxml"}
+*/
+
+class TestIOProcessor : public uscxml::EventIOProcessor, public uscxml::InterpreterMonitor {
+public:
+
+
+ static int lastToken;
+ static std::map<std::string, std::pair<uscxml::Interpreter*, evhttp_request*> > _interpreters;
+
+ TestIOProcessor() {}
+
+ virtual void onStableConfiguration(uscxml::Interpreter* interpreter) {
+ Arabica::XPath::NodeSet<std::string> configuration = interpreter->getConfiguration();
+
+ uscxml::Data reply;
+ reply.compound["sessionToken"] = uscxml::Data(interpreter->getName());
+ std::string seperator;
+ for (size_t i = 0; i < configuration.size(); i++) {
+ reply.compound["nextConfiguration"].array.push_back(uscxml::Data(ATTR(configuration[i], "id"), uscxml::Data::VERBATIM));
+ }
+
+ std::cout << "---- reply:" << std::endl;
+ std::cout << reply << std::endl;
+
+ std::stringstream replyString;
+ replyString << reply;
+
+ struct evbuffer *databuf = evbuffer_new();
+ evbuffer_add(databuf, replyString.str().c_str(), replyString.str().length());
+ evhttp_send_reply(_interpreters[interpreter->getName()].second, 200, "OK", databuf);
+ evbuffer_free(databuf);
+
+ }
+
+ virtual void afterCompletion(uscxml::Interpreter* interpreter) {
+// evhttp_request_free(_interpreterToRequest[interpreter]);
+ }
+
+ virtual void httpRecvReq(struct evhttp_request *req) {
+
+ std::cout << "---- received:" << std::endl;
+
+ if (evhttp_request_get_command(req) != EVHTTP_REQ_POST)
+ return;
+
+ evhttp_request_own(req);
+
+ struct evkeyval *header;
+ struct evkeyvalq *headers;
+ headers = evhttp_request_get_input_headers(req);
+
+ for (header = headers->tqh_first; header;
+ header = header->next.tqe_next) {
+ std::cout << header->key << ": " << header->value << std::endl;
+ }
+
+ std::string content;
+ struct evbuffer *buf;
+ 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);
+ }
+ }
+
+ uscxml::Data jsonReq = uscxml::Data::fromJSON(content);
+ std::cout << jsonReq << std::endl;
+
+
+ // is this a load request?
+ if (jsonReq.compound.find("load") != jsonReq.compound.end()) {
+ std::string filename = jsonReq.compound["load"].atom;
+ std::cout << "Starting Interpreter with " << filename << std::endl;
+ uscxml::Interpreter* interpreter = uscxml::Interpreter::fromURI(filename);
+ if (interpreter) {
+ std::string token = uscxml::toStr(lastToken++);
+ assert(_interpreters.find(token) == _interpreters.end());
+ interpreter->setName(token);
+ interpreter->addMonitor(this);
+ interpreter->start();
+ _interpreters[token] = std::make_pair(interpreter, req);
+ }
+ return;
+ }
+
+ if(jsonReq.compound.find("event") != jsonReq.compound.end()) {
+ assert(jsonReq.compound["event"].compound.find("sessionToken") != jsonReq.compound["event"].compound.end());
+ std::string token = jsonReq.compound["event"].compound["sessionToken"].atom;
+ assert(_interpreters.find(token) != _interpreters.end());
+ uscxml::Event event;
+ event.type = uscxml::Event::INTERNAL;
+ event.name = jsonReq.compound["event"].compound["name"].atom;
+ std::cout << "Sending event " << event << std::endl;
+// evhttp_request_free(_interpreters[token].second);
+ _interpreters[token].second = req;
+ _interpreters[token].first->receive(event);
+ }
+
+ }
+
+ std::string getPath() {
+ return "test";
+ }
+
+ void setURL(const std::string& url) {
+ std::cout << "Listening at " << url << std::endl;
+ _url = url;
+ }
+};
+
+int TestIOProcessor::lastToken;
+std::map<std::string, std::pair<uscxml::Interpreter*, evhttp_request*> > TestIOProcessor::_interpreters;
+
+int main(int argc, char** argv) {
+ TestIOProcessor* testServer = new TestIOProcessor();
+ uscxml::EventIOServer::registerProcessor(testServer);
+
+ while(true)
+ tthread::this_thread::sleep_for(tthread::chrono::milliseconds(20));
+
+// uscxml::Interpreter* interpreter = uscxml::Interpreter::fromURI(argv[1]);
+// interpreter->dump();
+// interpreter->interpret();
+} \ No newline at end of file
diff --git a/test/src/test-url.cpp b/test/src/test-url.cpp
index 4e94455..e3ca1e3 100644
--- a/test/src/test-url.cpp
+++ b/test/src/test-url.cpp
@@ -1,4 +1,5 @@
#include "uscxml/URL.h"
+#include "uscxml/Message.h"
#include <assert.h>
#include <boost/algorithm/string.hpp>
#include <iostream>
@@ -7,6 +8,24 @@ using namespace uscxml;
using namespace boost;
int main(int argc, char** argv) {
+
+ {
+ Data data = Data::fromJSON("asdf");
+ std::cout << data << std::endl;
+ }
+ {
+ Data data = Data::fromJSON("[ '1', '2', '3', '4' ]");
+ std::cout << data << std::endl;
+ }
+ {
+ Data data = Data::fromJSON("{'foo1': 'bar2', 'foo3': { 'foo4': 'bar5' }, 'foo6': 'bar7', 'foo8': { 'foo9': 'foo10': { 'foo11': 'bar12' } } }");
+ std::cout << data << std::endl;
+ }
+ {
+ Data data = Data::fromJSON("{\"firstName\": \"John\", \"lastName\": \"Smith\", \"age\": 25, \"address\": { \"streetAddress\": \"21 2nd Street\", \"city\": \"New York\",\"state\": \"NY\",\"postalCode\": 10021},\"phoneNumber\": [{\"type\": \"home\",\"number\": \"212 555-1234\"},{ \"type\": \"fax\",\"number\": \"646 555-4567\"}]}");
+ std::cout << data << std::endl;
+ }
+
{
URL url("http://www.heise.de/index.html");
std::cout << url.asString() << std::endl;