diff options
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; |