diff --git a/cyberiadaml.c b/cyberiadaml.c index 9dedfb6..ddac345 100644 --- a/cyberiadaml.c +++ b/cyberiadaml.c @@ -25,66 +25,143 @@ #include #include #include +#include +#include #include +#include #include "cyberiadaml.h" #include "utf8enc.h" -#define GRAPHML_NAMESPACE_URI "http://graphml.graphdrawing.org/xmlns" -#define GRAPHML_NAMESPACE_URI_YED "http://www.yworks.com/xml/graphml" -#define GRAPHML_GRAPHML_ELEMENT "graphml" -#define GRAPHML_BERLOGA_SCHEMENAME_ATTR "SchemeName" -#define GRAPHML_GRAPH_ELEMENT "graph" -#define GRAPHML_NODE_ELEMENT "node" -#define GRAPHML_EDGE_ELEMENT "edge" -#define GRAPHML_DATA_ELEMENT "data" -#define GRAPHML_KEY_ELEMENT "key" -#define GRAPHML_ID_ATTRIBUTE "id" -#define GRAPHML_KEY_ATTRIBUTE "key" -#define GRAPHML_FOR_ATTRIBUTE "for" -#define GRAPHML_ATTR_NAME_ATTRIBUTE "attr.name" -#define GRAPHML_ATTR_TYPE_ATTRIBUTE "attr.type" -#define GRAPHML_ALL_ATTRIBUTE_VALUE "all" -#define GRAPHML_SOURCE_ATTRIBUTE "source" -#define GRAPHML_TARGET_ATTRIBUTE "target" -#define GRAPHML_GEOM_X_ATTRIBUTE "x" -#define GRAPHML_GEOM_Y_ATTRIBUTE "y" -#define GRAPHML_GEOM_WIDTH_ATTRIBUTE "width" -#define GRAPHML_GEOM_HEIGHT_ATTRIBUTE "height" -#define GRAPHML_YED_GEOMETRYNODE "Geometry" -#define GRAPHML_YED_PATHNODE "Path" -#define GRAPHML_YED_POINTNODE "Point" -#define GRAPHML_YED_GEOM_SOURCE_X_ATTRIBUTE "sx" -#define GRAPHML_YED_GEOM_SOURCE_Y_ATTRIBUTE "sy" -#define GRAPHML_YED_GEOM_TARGET_X_ATTRIBUTE "tx" -#define GRAPHML_YED_GEOM_TARGET_Y_ATTRIBUTE "ty" -#define GRAPHML_YED_COMMENTNODE "UMLNoteNode" -#define GRAPHML_YED_GROUPNODE "GroupNode" -#define GRAPHML_YED_GENERICNODE "GenericNode" -#define GRAPHML_YED_LABELNODE "NodeLabel" -#define GRAPHML_YED_NODE_CONFIG_ATTRIBUTE "configuration" -#define GRAPHML_YED_NODE_CONFIG_START "com.yworks.bpmn.Event" -#define GRAPHML_YED_NODE_CONFIG_START2 "com.yworks.bpmn.Event.withShadow" -#define GRAPHML_YED_PROPNODE "Property" -#define GRAPHML_YED_PROP_VALUE_ATTRIBUTE "value" -#define GRAPHML_YED_PROP_VALUE_START "EVENT_CHARACTERISTIC_START" -#define GRAPHML_YED_EDGELABEL "EdgeLabel" -#define GRAPHML_CYB_GRAPH_FORMAT "gFormat" -#define GRAPHML_CYB_DATA_NAME "dName" -#define GRAPHML_CYB_DATA_DATA "dData" -#define GRAPHML_CYB_DATA_INITIAL "dInitial" -#define GRAPHML_CYB_DATA_GEOMETRY "dGeometry" -#define GRAPHML_CYB_DATA_COLOR "dColor" +/* ----------------------------------------------------------------------------- + * Cyberiada parser constants + * ----------------------------------------------------------------------------- */ -#define CYBERIADA_HOLLOW_NODE "" -#define CYBERIADA_VERSION_CYBERIADAML "Cyberiada-GraphML" -#define CYBERIADA_VERSION_BERLOGA "yEd Berloga 1.4" -#define CYBERIADA_VERSION_OSTRANNA "yEd Ostranna" +/* Core GraphML constants */ -#define MAX_STR_LEN 4096 +#define GRAPHML_XML_ENCODING "utf-8" +#define GRAPHML_NAMESPACE_URI "http://graphml.graphdrawing.org/xmlns" +#define GRAPHML_GRAPHML_ELEMENT "graphml" +#define GRAPHML_GRAPH_ELEMENT "graph" +#define GRAPHML_NODE_ELEMENT "node" +#define GRAPHML_EDGE_ELEMENT "edge" +#define GRAPHML_DATA_ELEMENT "data" +#define GRAPHML_KEY_ELEMENT "key" +#define GRAPHML_ID_ATTRIBUTE "id" +#define GRAPHML_KEY_ATTRIBUTE "key" +#define GRAPHML_FOR_ATTRIBUTE "for" +#define GRAPHML_PORT_ATTRIBUTE "port" +#define GRAPHML_EDGEDEFAULT_ATTRIBUTE "edgedefault" +#define GRAPHML_ATTR_NAME_ATTRIBUTE "attr.name" +#define GRAPHML_ATTR_TYPE_ATTRIBUTE "attr.type" +#define GRAPHML_ALL_ATTRIBUTE_VALUE "all" +#define GRAPHML_EDGEDEFAULT_ATTRIBUTE_VALUE "directed" +#define GRAPHML_SOURCE_ATTRIBUTE "source" +#define GRAPHML_TARGET_ATTRIBUTE "target" -#define PSEUDO_NODE_SIZE 20 -#define COMMENT_TITLE "" +/* Common GraphML constants */ + +#define GRAPHML_GEOM_X_ATTRIBUTE "x" +#define GRAPHML_GEOM_Y_ATTRIBUTE "y" +#define GRAPHML_GEOM_WIDTH_ATTRIBUTE "width" +#define GRAPHML_GEOM_HEIGHT_ATTRIBUTE "height" + +/* YED format constants */ + +#define GRAPHML_NAMESPACE_URI_YED "http://www.yworks.com/xml/graphml" +#define GRAPHML_BERLOGA_SCHEMENAME_ATTR "SchemeName" +#define GRAPHML_YED_YFILES_TYPE_ATTR "yfiles.type" +#define GRAPHML_YED_GEOMETRYNODE "Geometry" +#define GRAPHML_YED_BORDERSTYLENODE "BorderStyle" +#define GRAPHML_YED_LINESTYLENODE "LineStyle" +#define GRAPHML_YED_FILLNODE "Fill" +#define GRAPHML_YED_PATHNODE "Path" +#define GRAPHML_YED_POINTNODE "Point" +#define GRAPHML_YED_GEOM_SOURCE_X_ATTRIBUTE "sx" +#define GRAPHML_YED_GEOM_SOURCE_Y_ATTRIBUTE "sy" +#define GRAPHML_YED_GEOM_TARGET_X_ATTRIBUTE "tx" +#define GRAPHML_YED_GEOM_TARGET_Y_ATTRIBUTE "ty" +#define GRAPHML_YED_COMMENTNODE "UMLNoteNode" +#define GRAPHML_YED_GROUPNODE "GroupNode" +#define GRAPHML_YED_GENERICNODE "GenericNode" +#define GRAPHML_YED_LABELNODE "NodeLabel" +#define GRAPHML_YED_NODE_CONFIG_ATTRIBUTE "configuration" +#define GRAPHML_YED_NODE_CONFIG_START "com.yworks.bpmn.Event" +#define GRAPHML_YED_NODE_CONFIG_START2 "com.yworks.bpmn.Event.withShadow" +#define GRAPHML_YED_PROPNODE "Property" +#define GRAPHML_YED_PROP_VALUE_ATTRIBUTE "value" +#define GRAPHML_YED_PROP_VALUE_START "EVENT_CHARACTERISTIC_START" +#define GRAPHML_YED_EDGELABEL "EdgeLabel" +#define GRAPHML_YED_POLYLINEEDGE "PolyLineEdge" + +/* Cyberiada-GraphML format constants */ + +#define GRAPHML_CYB_GRAPH_FORMAT "gFormat" +#define GRAPHML_CYB_DATA_NAME "dName" +#define GRAPHML_CYB_DATA_DATA "dData" +#define GRAPHML_CYB_DATA_COMMENT "dNote" +#define GRAPHML_CYB_DATA_COMMENT_SUBJECT "dPivot" +#define GRAPHML_CYB_DATA_INITIAL "dInitial" +#define GRAPHML_CYB_DATA_GEOMETRY "dGeometry" +#define GRAPHML_CYB_DATA_COLOR "dColor" +#define GRAPHML_CYB_COMMENT_FORMAL "formal" +#define GRAPHML_CYB_COMMENT_INFORMAL "informal" + +/* HSM format / standard constants */ + +#define CYBERIADA_FORMAT_CYBERIADAML "Cyberiada-GraphML-1.0" +#define CYBERIADA_FORMAT_BERLOGA "yEd Berloga" +#define CYBERIADA_FORMAT_OSTRANNA "yEd Ostranna" +#define CYBERIADA_STANDARD_VERSION_CYBERIADAML "1.0" +#define CYBERIADA_STANDARD_VERSION_YED "" + +/* HSM behavior constants */ + +#define CYBERIADA_BEHAVIOR_NEWLINE "\n\n" +#define CYBERIADA_BEHAVIOR_NEWLINE_RN "\r\n\r\n" +#define CYBERIADA_BEHAVIOR_TRIGGER_ENTRY "entry" +#define CYBERIADA_BEHAVIOR_TRIGGER_EXIT "exit" +#define CYBERIADA_BEHAVIOR_TRIGGER_DO "do" +#define CYBERIADA_BEHAVIOR_EDGE_REGEXP "^\\s*(\\w((\\w| |\\.)*\\w)?(\\(\\w+\\))?)?\\s*(\\[([^]]+)\\])?\\s*(propagate|block)?\\s*(/\\s*(.*))?\\s*$" +#define CYBERIADA_BEHAVIOR_NODE_REGEXP "^\\s*(\\w((\\w| |\\.)*\\w)?(\\(\\w+\\))?)\\s*(\\[([^]]+)\\])?\\s*(propagate|block)?\\s*(/\\s*(.*)?)\\s*$" +#define CYBERIADA_BEHAVIOR_REGEXP_MATCHES 10 +#define CYBERIADA_BEHAVIOR_REGEXP_MATCH_TRIGGER 1 +#define CYBERIADA_BEHAVIOR_REGEXP_MATCH_GUARD 6 +#define CYBERIADA_BEHAVIOR_REGEXP_MATCH_PROP 7 +#define CYBERIADA_BEHAVIOR_REGEXP_MATCH_ACTION 9 +#define CYBERIADA_BEHAVIOR_SPACES_REGEXP "^\\s*$" +/*#define CYBERIADA_BEHAVIOR_NEWLINE_REGEXP "^([^\n]*(\n[ \t\r]*[^\\s])?)*\n\\s*\n(.*)?$" + #define CYBERIADA_BEHAVIOR_NL_REGEXP_MATCHES 4*/ + +/* HSM metadata constants */ + +#define CYBERIADA_META_SEPARATOR_CHR '/' +#define CYBERIADA_META_NEW_LINE_CHR '\n' +#define CYBERIADA_META_NEW_LINE_STR "\n" +#define CYBERIADA_META_STANDARD_VERSION "standardVersion" +#define CYBERIADA_META_PLATFORM_NAME "platform" +#define CYBERIADA_META_PLATFORM_VERSION "platformVersion" +#define CYBERIADA_META_PLATFORM_LANGUAGE "platformLanguage" +#define CYBERIADA_META_TARGET_SYSTEM "target" +#define CYBERIADA_META_NAME "name" +#define CYBERIADA_META_AUTHOR "author" +#define CYBERIADA_META_CONTACT "contact" +#define CYBERIADA_META_DESCRIPTION "description" +#define CYBERIADA_META_VERSION "version" +#define CYBERIADA_META_DATE "date" +#define CYBERIADA_META_ACTIONS_ORDER "actionsOrder" +#define CYBERIADA_META_AO_TRANSITION "transitionFirst" +#define CYBERIADA_META_AO_EXIT "exitFirst" +#define CYBERIADA_META_EVENT_PROPAGATION "eventPropagation" +#define CYBERIADA_META_EP_PROPAGATE "propagate" +#define CYBERIADA_META_EP_BLOCK "block" + +/* Misc. constants */ + +#define CYBERIADA_ROOT_NODE "__ROOT__" +#define MAX_STR_LEN 4096 +#define PSEUDO_NODE_SIZE 20 +#define EMPTY_TITLE "" #ifdef __DEBUG__ #define DEBUG(...) fprintf(stderr, __VA_ARGS__) @@ -123,7 +200,7 @@ static int cyberiada_copy_string(char** target, size_t* size, const char* source * Graph manipulation functions * ----------------------------------------------------------------------------- */ -static CyberiadaNode* cyberiada_graph_find_node(CyberiadaNode* root, const char* id) +static CyberiadaNode* cyberiada_graph_find_node_by_id(CyberiadaNode* root, const char* id) { CyberiadaNode* node; CyberiadaNode* found; @@ -131,12 +208,30 @@ static CyberiadaNode* cyberiada_graph_find_node(CyberiadaNode* root, const char* return root; } for (node = root->next; node; node = node->next) { - found = cyberiada_graph_find_node(node, id); + found = cyberiada_graph_find_node_by_id(node, id); if (found) return found; } if (root->children) { - return cyberiada_graph_find_node(root->children, id); + return cyberiada_graph_find_node_by_id(root->children, id); + } + return NULL; +} + +static CyberiadaNode* cyberiada_graph_find_node_by_type(CyberiadaNode* root, CyberiadaNodeTypeMask mask) +{ + CyberiadaNode* node; + CyberiadaNode* found; + if (root->type & mask) { + return root; + } + for (node = root->next; node; node = node->next) { + found = cyberiada_graph_find_node_by_type(node, mask); + if (found) + return found; + } + if (root->children) { + return cyberiada_graph_find_node_by_type(root->children, mask); } return NULL; } @@ -235,21 +330,6 @@ static CyberiadaBehavior* cyberiada_new_behavior(CyberiadaBehaviorType type, return behavior; } -#define BEHAVIOR_NEWLINE "\n\n" -#define BEHAVIOR_NEWLINE2 "\r\n\r\n" -#define BEHAVIOR_TRIGGER_ENTRY "entry" -#define BEHAVIOR_TRIGGER_EXIT "exit" -#define BEHAVIOR_TRIGGER_DO "do" -#define BEHAVIOR_EDGE_REGEXP "^\\s*(\\w((\\w| |\\.)*\\w)?(\\(\\w+\\))?)?\\s*(\\[([^]]+)\\])?\\s*(/\\s*(.*))?\\s*$" -#define BEHAVIOR_NODE_REGEXP "^\\s*(\\w((\\w| |\\.)*\\w)?(\\(\\w+\\))?)\\s*(\\[([^]]+)\\])?\\s*(/\\s*(.*)?)\\s*$" -#define BEHAVIOR_REGEXP_MATCHES 9 -#define BEHAVIOR_REGEXP_MATCH_TRIGGER 1 -#define BEHAVIOR_REGEXP_MATCH_GUARD 6 -#define BEHAVIOR_REGEXP_MATCH_ACTION 8 -#define BEHAVIOR_SPACES_REGEXP "^\\s*$" -/*#define BEHAVIOR_NEWLINE_REGEXP "^([^\n]*(\n[ \t\r]*[^\\s])?)*\n\\s*\n(.*)?$" - #define BEHAVIOR_NL_REGEXP_MATCHES 4*/ - static regex_t cyberiada_edge_behavior_regexp; static regex_t cyberiada_node_behavior_regexp; /*static regex_t cyberiada_newline_regexp;*/ @@ -257,19 +337,19 @@ static regex_t cyberiada_spaces_regexp; static int cyberiada_init_behavior_regexps(void) { - if (regcomp(&cyberiada_edge_behavior_regexp, BEHAVIOR_EDGE_REGEXP, REG_EXTENDED)) { + if (regcomp(&cyberiada_edge_behavior_regexp, CYBERIADA_BEHAVIOR_EDGE_REGEXP, REG_EXTENDED)) { ERROR("cannot compile edge behavior regexp\n"); return CYBERIADA_ASSERT; } - if (regcomp(&cyberiada_node_behavior_regexp, BEHAVIOR_NODE_REGEXP, REG_EXTENDED)) { + if (regcomp(&cyberiada_node_behavior_regexp, CYBERIADA_BEHAVIOR_NODE_REGEXP, REG_EXTENDED)) { ERROR("cannot compile edge behavior regexp\n"); return CYBERIADA_ASSERT; } -/* if (regcomp(&cyberiada_newline_regexp, BEHAVIOR_NEWLINE_REGEXP, REG_EXTENDED)) { +/* if (regcomp(&cyberiada_newline_regexp, CYBERIADA_BEHAVIOR_NEWLINE_REGEXP, REG_EXTENDED)) { ERROR("cannot compile new line regexp\n"); return CYBERIADA_ASSERT; }*/ - if (regcomp(&cyberiada_spaces_regexp, BEHAVIOR_SPACES_REGEXP, REG_EXTENDED)) { + if (regcomp(&cyberiada_spaces_regexp, CYBERIADA_BEHAVIOR_SPACES_REGEXP, REG_EXTENDED)) { ERROR("cannot compile new line regexp\n"); return CYBERIADA_ASSERT; } @@ -294,15 +374,15 @@ static int cyberiaga_matchres_behavior_regexps(const char* text, char* part; int start, end; - if (pmatch_size != BEHAVIOR_REGEXP_MATCHES) { + if (pmatch_size != CYBERIADA_BEHAVIOR_REGEXP_MATCHES) { ERROR("bad behavior regexp match array size\n"); return CYBERIADA_ASSERT; } for (i = 0; i < pmatch_size; i++) { - if (i != BEHAVIOR_REGEXP_MATCH_TRIGGER && - i != BEHAVIOR_REGEXP_MATCH_GUARD && - i != BEHAVIOR_REGEXP_MATCH_ACTION) { + if (i != CYBERIADA_BEHAVIOR_REGEXP_MATCH_TRIGGER && + i != CYBERIADA_BEHAVIOR_REGEXP_MATCH_GUARD && + i != CYBERIADA_BEHAVIOR_REGEXP_MATCH_ACTION) { continue; } start = pmatch[i].rm_so; @@ -314,9 +394,9 @@ static int cyberiaga_matchres_behavior_regexps(const char* text, } else { part = ""; } - if (i == BEHAVIOR_REGEXP_MATCH_TRIGGER) { + if (i == CYBERIADA_BEHAVIOR_REGEXP_MATCH_TRIGGER) { *trigger = part; - } else if (i == BEHAVIOR_REGEXP_MATCH_GUARD) { + } else if (i == CYBERIADA_BEHAVIOR_REGEXP_MATCH_GUARD) { *guard = part; } else { /* i == BEHAVIOR_REGEXP_MATCH_ACTION */ @@ -369,12 +449,12 @@ static int cyberiada_decode_edge_behavior(const char* text, CyberiadaBehavior** size_t buffer_len; char *trigger = "", *guard = "", *action = ""; char *buffer; - regmatch_t pmatch[BEHAVIOR_REGEXP_MATCHES]; + regmatch_t pmatch[CYBERIADA_BEHAVIOR_REGEXP_MATCHES]; buffer = utf8_encode(text, strlen(text), &buffer_len); if ((res = regexec(&cyberiada_edge_behavior_regexp, buffer, - BEHAVIOR_REGEXP_MATCHES, pmatch, 0)) != 0) { + CYBERIADA_BEHAVIOR_REGEXP_MATCHES, pmatch, 0)) != 0) { if (res == REG_NOMATCH) { ERROR("edge behavior text didn't match the regexp\n"); return CYBERIADA_BEHAVIOR_FORMAT_ERROR; @@ -383,15 +463,15 @@ static int cyberiada_decode_edge_behavior(const char* text, CyberiadaBehavior** return CYBERIADA_ASSERT; } } - if (cyberiaga_matchres_behavior_regexps(text, - pmatch, BEHAVIOR_REGEXP_MATCHES, + if (cyberiaga_matchres_behavior_regexps(buffer, + pmatch, CYBERIADA_BEHAVIOR_REGEXP_MATCHES, &trigger, &guard, &action) != CYBERIADA_NO_ERROR) { return CYBERIADA_ASSERT; } decode_utf8_strings(&trigger, &guard, &action); - /*DEBUG("\n"); +/* DEBUG("\n"); DEBUG("edge behavior:\n"); DEBUG("trigger: %s\n", trigger); DEBUG("guard: %s\n", guard); @@ -414,11 +494,11 @@ static int cyberiada_add_behavior(const char* trigger, const char* guard, const CyberiadaBehaviorType type; if (trigger) { - if (strcmp(trigger, BEHAVIOR_TRIGGER_ENTRY) == 0) { + if (strcmp(trigger, CYBERIADA_BEHAVIOR_TRIGGER_ENTRY) == 0) { type = cybBehaviorEntry; - } else if (strcmp(trigger, BEHAVIOR_TRIGGER_EXIT) == 0) { + } else if (strcmp(trigger, CYBERIADA_BEHAVIOR_TRIGGER_EXIT) == 0) { type = cybBehaviorExit; - } else if (strcmp(trigger, BEHAVIOR_TRIGGER_DO) == 0) { + } else if (strcmp(trigger, CYBERIADA_BEHAVIOR_TRIGGER_DO) == 0) { type = cybBehaviorDo; } else { type = cybBehaviorTransition; @@ -447,9 +527,9 @@ static int cyberiada_decode_state_block_behavior(const char* text, CyberiadaBeha { int res; char *trigger = "", *guard = "", *action = ""; - regmatch_t pmatch[BEHAVIOR_REGEXP_MATCHES]; + regmatch_t pmatch[CYBERIADA_BEHAVIOR_REGEXP_MATCHES]; if ((res = regexec(&cyberiada_node_behavior_regexp, text, - BEHAVIOR_REGEXP_MATCHES, pmatch, 0)) != 0) { + CYBERIADA_BEHAVIOR_REGEXP_MATCHES, pmatch, 0)) != 0) { if (res == REG_NOMATCH) { ERROR("node block behavior text didn't match the regexp\n"); return CYBERIADA_BEHAVIOR_FORMAT_ERROR; @@ -459,7 +539,7 @@ static int cyberiada_decode_state_block_behavior(const char* text, CyberiadaBeha } } if (cyberiaga_matchres_behavior_regexps(text, - pmatch, BEHAVIOR_REGEXP_MATCHES, + pmatch, CYBERIADA_BEHAVIOR_REGEXP_MATCHES, &trigger, &guard, &action) != CYBERIADA_NO_ERROR) { return CYBERIADA_ASSERT; } @@ -488,8 +568,8 @@ static int cyberiada_decode_state_behavior(const char* text, CyberiadaBehavior** while (*next) { start = next; - block = strstr(start, BEHAVIOR_NEWLINE); - block2 = strstr(start, BEHAVIOR_NEWLINE2); + block = strstr(start, CYBERIADA_BEHAVIOR_NEWLINE); + block2 = strstr(start, CYBERIADA_BEHAVIOR_NEWLINE_RN); if (block2 && ((block && (block > block2)) || !block)) { block = block2; *block2 = 0; @@ -538,34 +618,6 @@ static int cyberiada_decode_state_behavior(const char* text, CyberiadaBehavior** return CYBERIADA_NO_ERROR; } -/*static CyberiadaExtension* cyberiada_new_extension(const char* id, const char* title, const char* data) -{ - CyberiadaExtension* new_ext = (CyberiadaExtension*)malloc(sizeof(CyberiadaExtension)); - cyberiada_copy_string(&(new_ext->id), &(new_ext->id_len), id); - cyberiada_copy_string(&(new_ext->title), &(new_ext->title_len), title); - cyberiada_copy_string(&(new_ext->data), &(new_ext->data_len), data); - new_ext->next = NULL; - return new_ext; -} - -static int cyberiada_graph_add_extension(CyberiadaSM* sm, const char* id, const char* title, const char* data) -{ - CyberiadaExtension* last_ext; - CyberiadaExtension* new_ext; - if (!sm) { - return CYBERIADA_BAD_PARAMETER; - } - new_ext = cyberiada_new_extension(id, title, data); - last_ext = sm->extensions; - if (last_ext == NULL) { - sm->extensions = new_ext; - } else { - while (last_ext->next) last_ext = last_ext->next; - last_ext->next = new_ext; - } - return CYBERIADA_NO_ERROR; -}*/ - static int cyberiada_graph_add_sibling_node(CyberiadaNode* sibling, CyberiadaNode* new_node) { CyberiadaNode* node = sibling; @@ -673,7 +725,6 @@ static CyberiadaEdge* cyberiada_graph_find_last_edge(CyberiadaSM* sm) return edge; } - /*static int cyberiada_graph_add_edge_behavior(CyberiadaEdge* edge, CyberiadaBehaviorType type, const char* trigger, @@ -718,13 +769,12 @@ static int cyberiada_destroy_edge(CyberiadaEdge* e) return CYBERIADA_NO_ERROR; } - static int cyberiada_graph_reconstruct_edges(CyberiadaSM* sm) { CyberiadaEdge* edge = sm->edges; while (edge) { - CyberiadaNode* source = cyberiada_graph_find_node(sm->nodes, edge->source_id); - CyberiadaNode* target = cyberiada_graph_find_node(sm->nodes, edge->target_id); + CyberiadaNode* source = cyberiada_graph_find_node_by_id(sm->nodes, edge->source_id); + CyberiadaNode* target = cyberiada_graph_find_node_by_id(sm->nodes, edge->target_id); if (!source || !target) { ERROR("cannot find source/target node for edge %s %s\n", edge->source_id, edge->target_id); return CYBERIADA_FORMAT_ERROR; @@ -736,6 +786,224 @@ static int cyberiada_graph_reconstruct_edges(CyberiadaSM* sm) return CYBERIADA_NO_ERROR; } +static int cyberiada_destroy_meta(CyberiadaMetainformation* meta) +{ + if (meta) { + if (meta->standard_version) free(meta->standard_version); + if (meta->platform_name) free(meta->platform_name); + if (meta->platform_version) free(meta->platform_version); + if (meta->platform_language) free(meta->platform_language); + if (meta->target_system) free(meta->target_system); + if (meta->name) free(meta->name); + if (meta->author) free(meta->author); + if (meta->contact) free(meta->contact); + if (meta->description) free(meta->description); + if (meta->version) free(meta->version); + if (meta->date) free(meta->date); + free(meta); + } + return CYBERIADA_NO_ERROR; +} + +typedef struct { + const char* name; + size_t value_offset; + size_t len_offset; + const char* title; +} MetainfoDeclaration; + +static MetainfoDeclaration cyberiada_metadata[] = { + { + CYBERIADA_META_STANDARD_VERSION, + offsetof(CyberiadaMetainformation, standard_version), + offsetof(CyberiadaMetainformation, standard_version_len), + "standard version" + }, { + CYBERIADA_META_PLATFORM_NAME, + offsetof(CyberiadaMetainformation, platform_name), + offsetof(CyberiadaMetainformation, platform_name_len), + "platform name" + }, { + CYBERIADA_META_PLATFORM_VERSION, + offsetof(CyberiadaMetainformation, platform_version), + offsetof(CyberiadaMetainformation, platform_version_len), + "platform version" + }, { + CYBERIADA_META_PLATFORM_LANGUAGE, + offsetof(CyberiadaMetainformation, platform_language), + offsetof(CyberiadaMetainformation, platform_language_len), + "platform language" + }, { + CYBERIADA_META_TARGET_SYSTEM, + offsetof(CyberiadaMetainformation, target_system), + offsetof(CyberiadaMetainformation, target_system_len), + "target system" + }, { + CYBERIADA_META_NAME, + offsetof(CyberiadaMetainformation, name), + offsetof(CyberiadaMetainformation, name_len), + "document name" + }, { + CYBERIADA_META_AUTHOR, + offsetof(CyberiadaMetainformation, author), + offsetof(CyberiadaMetainformation, author_len), + "document author" + }, { + CYBERIADA_META_CONTACT, + offsetof(CyberiadaMetainformation, contact), + offsetof(CyberiadaMetainformation, contact_len), + "document author's contact" + }, { + CYBERIADA_META_DESCRIPTION, + offsetof(CyberiadaMetainformation, description), + offsetof(CyberiadaMetainformation, description_len), + "document description" + }, { + CYBERIADA_META_VERSION, + offsetof(CyberiadaMetainformation, version), + offsetof(CyberiadaMetainformation, version_len), + "document version" + }, { + CYBERIADA_META_DATE, + offsetof(CyberiadaMetainformation, date), + offsetof(CyberiadaMetainformation, date_len), + "document date" + }, { + CYBERIADA_META_DATE, + offsetof(CyberiadaMetainformation, date), + offsetof(CyberiadaMetainformation, date_len), + "actions order" + } +}; + +static int cyberiada_add_default_meta(CyberiadaSM* sm, const char* sm_name) +{ + CyberiadaMetainformation* meta; + + if (sm->meta_info) { + return CYBERIADA_BAD_PARAMETER; + } + + meta = (CyberiadaMetainformation*)malloc(sizeof(CyberiadaMetainformation)); + memset(meta, 0, sizeof(CyberiadaMetainformation)); + + cyberiada_copy_string(&(meta->standard_version), + &(meta->standard_version_len), + CYBERIADA_STANDARD_VERSION_YED); + if (*sm_name) { + cyberiada_copy_string(&(meta->name), &(meta->name_len), sm_name); + } + + meta->actions_order_flag = 1; + meta->event_propagation_flag = 1; + + sm->meta_info = meta; + return CYBERIADA_NO_ERROR; +} + +/*static int cyberiada_add_meta(CyberiadaSM* sm, char* metadata) +{ + CyberiadaMetainformation* meta; + char *line, *parts; + size_t i; + char found; + + if (sm->meta_info) { + return CYBERIADA_BAD_PARAMETER; + } + + meta = (CyberiadaMetainformation*)malloc(sizeof(CyberiadaMetainformation)); + memset(meta, 0, sizeof(CyberiadaMetainformation)); + + if (strchr(metadata, CYBERIADA_META_NEW_LINE_CHR)) { + line = strtok(metadata, CYBERIADA_META_NEW_LINE_STR); + } else { + line = metadata; + } + do { + parts = strchr(metadata, CYBERIADA_META_SEPARATOR_CHR); + if (parts == NULL) { + ERROR("Error decoding SM metainformation: cannot find separator\n"); + cyberiada_destroy_meta(meta); + return CYBERIADA_METADATA_FORMAT_ERROR; + } + *parts = 0; + do { + parts++; + } while (isspace(*parts)); + + found = 0; + for (i = 0; i < sizeof(cyberiada_metadata) / sizeof(MetainfoDeclaration); i++) { + if (strcmp(line, cyberiada_metadata[i].name) == 0) { + if ((void*)((&meta) + cyberiada_metadata[i].value_offset) != NULL) { + ERROR("Error decoding SM metainformation: double parameter %s\n", + cyberiada_metadata[i].title); + cyberiada_destroy_meta(meta); + return CYBERIADA_METADATA_FORMAT_ERROR; + } + cyberiada_copy_string((char**)((char*)meta + cyberiada_metadata[i].value_offset), + (size_t*)((char*)meta + cyberiada_metadata[i].len_offset), parts); + found = 1; + break; + } + } + + if (!found) { + if (strcmp(line, CYBERIADA_META_ACTIONS_ORDER) == 0) { + if (strcmp(parts, CYBERIADA_META_AO_TRANSITION) == 0) { + meta->actions_order_flag = 1; + } else if (strcmp(parts, CYBERIADA_META_AO_EXIT) == 0) { + meta->actions_order_flag = 2; + } else { + ERROR("Error decoding SM metainformation: bad value of actions order flag parameter\n"); + cyberiada_destroy_meta(meta); + return CYBERIADA_METADATA_FORMAT_ERROR; + } + } else if (strcmp(line, CYBERIADA_META_EVENT_PROPAGATION) == 0) { + if (strcmp(parts, CYBERIADA_META_EP_PROPAGATE) == 0) { + meta->event_propagation_flag = 1; + } else if (strcmp(parts, CYBERIADA_META_EP_BLOCK) == 0) { + meta->event_propagation_flag = 2; + } else { + ERROR("Error decoding SM metainformation: bad value of event propagation flag parameter\n"); + cyberiada_destroy_meta(meta); + return CYBERIADA_METADATA_FORMAT_ERROR; + } + } else { + ERROR("Error decoding SM metainformation: bad key %s\n", line); + cyberiada_destroy_meta(meta); + return CYBERIADA_METADATA_FORMAT_ERROR; + } + } + + line = strtok(NULL, CYBERIADA_META_NEW_LINE_STR); + } while (line); + + if (!meta->standard_version) { + ERROR("Error decoding SM metainformation: standard version is not set\n"); + cyberiada_destroy_meta(meta); + return CYBERIADA_METADATA_FORMAT_ERROR; + } + + if (strcmp(meta->standard_version, CYBERIADA_STANDARD_VERSION_CYBERIADAML) != 0) { + ERROR("Error decoding SM metainformation: unsupported version of Cyberiada standard - %s\n", + meta->standard_version); + cyberiada_destroy_meta(meta); + return CYBERIADA_METADATA_FORMAT_ERROR; + } + + // set default values + if (!meta->actions_order_flag) { + meta->actions_order_flag = 1; + } + if (!meta->event_propagation_flag) { + meta->event_propagation_flag = 1; + } + + sm->meta_info = meta; + return CYBERIADA_NO_ERROR; +}*/ + /* ----------------------------------------------------------------------------- * The Cyberiada GraphML library fucntions declarations * ----------------------------------------------------------------------------- */ @@ -750,15 +1018,10 @@ CyberiadaSM* cyberiada_create_sm(void) int cyberiada_init_sm(CyberiadaSM* sm) { if (sm) { - sm->name = NULL; - sm->name = 0; - sm->version = NULL; - sm->version_len = 0; - sm->info = NULL; - sm->info_len = 0; + sm->format = NULL; + sm->meta_info = NULL; sm->nodes = NULL; sm->edges = NULL; - /*sm->extensions = NULL;*/ } return CYBERIADA_NO_ERROR; } @@ -766,40 +1029,26 @@ int cyberiada_init_sm(CyberiadaSM* sm) int cyberiada_cleanup_sm(CyberiadaSM* sm) { CyberiadaEdge *edge, *e; - /*CyberiadaExtension *extension, *ext;*/ - - if (sm == NULL) { - return CYBERIADA_NO_ERROR; + if (sm) { + if (sm->format) { + free(sm->format); + } + if (sm->meta_info) { + cyberiada_destroy_meta(sm->meta_info); + } + if (sm->nodes) { + cyberiada_destroy_all_nodes(sm->nodes); + } + if (sm->edges) { + edge = sm->edges; + do { + e = edge; + edge = edge->next; + cyberiada_destroy_edge(e); + } while (edge); + } + cyberiada_init_sm(sm); } - - if (sm->name) free(sm->name); - if (sm->version) free(sm->version); - if (sm->info) free(sm->info); - if (sm->nodes) { - cyberiada_destroy_all_nodes(sm->nodes); - } - if (sm->edges) { - edge = sm->edges; - do { - e = edge; - edge = edge->next; - cyberiada_destroy_edge(e); - } while (edge); - } -/* if (sm->extensions) { - extension = sm->extensions; - do { - ext = extension; - extension = extension->next; - if (ext->id) free(ext->id); - if (ext->title) free(ext->title); - if (ext->data) free(ext->data); - free(ext); - } while(ext); - }*/ - - cyberiada_init_sm(sm); - return CYBERIADA_NO_ERROR; } @@ -968,9 +1217,10 @@ static GraphProcessorState handle_new_graph(xmlNode* xml_node, GRAPHML_ID_ATTRIBUTE) != CYBERIADA_NO_ERROR) { return gpsInvalid; } - DEBUG("found graph %s \n", buffer); + /* DEBUG("found graph %s \n", buffer); */ if (sm->nodes == NULL) { - sm->nodes = cyberiada_new_node(CYBERIADA_HOLLOW_NODE); + sm->nodes = cyberiada_new_node(CYBERIADA_ROOT_NODE); + sm->nodes->type = cybNodeSM; node_stack_set_top_node(stack, sm->nodes); } return gpsGraph; @@ -989,7 +1239,7 @@ static GraphProcessorState handle_new_node(xmlNode* xml_node, GRAPHML_ID_ATTRIBUTE) != CYBERIADA_NO_ERROR) { return gpsInvalid; } - DEBUG("found node %s\n", buffer); + /* DEBUG("found node %s\n", buffer); */ parent = node_stack_current_node(stack); if (parent == NULL) { ERROR("current node invalid\n"); @@ -1004,7 +1254,7 @@ static GraphProcessorState handle_new_node(xmlNode* xml_node, cyberiada_graph_add_sibling_node(parent->children, node); } - if (*(node->id) != 0 || sm->version) { + if (*(node->id) != 0 || sm->format) { return gpsNode; } else { return gpsFakeNode; @@ -1034,7 +1284,7 @@ static GraphProcessorState handle_comment_node(xmlNode* xml_node, return gpsInvalid; } current->type = cybNodeComment; - DEBUG("Set node type comment\n"); + /* DEBUG("Set node type comment\n"); */ /*cyberiada_copy_string(&(current->title), &(current->title_len), COMMENT_TITLE);*/ return gpsNodeGeometry; } @@ -1189,7 +1439,7 @@ static GraphProcessorState handle_node_title(xmlNode* xml_node, return gpsInvalid; } cyberiada_get_element_text(buffer, buffer_len, xml_node); - DEBUG("Set node %s title %s\n", current->id, buffer); + /* DEBUG("Set node %s title %s\n", current->id, buffer); */ cyberiada_copy_string(&(current->title), &(current->title_len), buffer); return gpsNodeAction; } @@ -1207,14 +1457,14 @@ static GraphProcessorState handle_node_behavior(xmlNode* xml_node, } cyberiada_get_element_text(buffer, buffer_len, xml_node); if (current->type == cybNodeComment) { - DEBUG("Set node %s comment text %s\n", current->id, buffer); + /* DEBUG("Set node %s comment text %s\n", current->id, buffer); */ cyberiada_copy_string(&(current->title), &(current->title_len), buffer); } else { if (current->behavior != NULL) { ERROR("Trying to set node %s behavior twice\n", current->id); return gpsInvalid; } - DEBUG("Set node %s behavior %s\n", current->id, buffer); + /* DEBUG("Set node %s behavior %s\n", current->id, buffer); */ if (cyberiada_decode_state_behavior(buffer, &(current->behavior)) != CYBERIADA_NO_ERROR) { ERROR("cannot decode node behavior\n"); return gpsInvalid; @@ -1238,7 +1488,7 @@ static GraphProcessorState handle_new_edge(xmlNode* xml_node, GRAPHML_ID_ATTRIBUTE) != CYBERIADA_NO_ERROR) { buffer[0] = 0; } - DEBUG("found edge %s\n", buffer); + /* DEBUG("found edge %s\n", buffer); */ if(cyberiada_get_attr_value(source_buffer, source_buffer_len, xml_node, GRAPHML_SOURCE_ATTRIBUTE) != CYBERIADA_NO_ERROR) { @@ -1249,7 +1499,7 @@ static GraphProcessorState handle_new_edge(xmlNode* xml_node, GRAPHML_TARGET_ATTRIBUTE) != CYBERIADA_NO_ERROR) { return gpsInvalid; } - DEBUG("add edge %s %s -> %s\n", buffer, source_buffer, target_buffer); + /* DEBUG("add edge %s %s -> %s\n", buffer, source_buffer, target_buffer); */ cyberiada_graph_add_edge(sm, buffer, source_buffer, target_buffer); return gpsEdge; } @@ -1329,8 +1579,8 @@ static GraphProcessorState handle_edge_label(xmlNode* xml_node, return gpsInvalid; } cyberiada_get_element_text(buffer, buffer_len, xml_node); - DEBUG("add edge %s:%s behavior %s\n", - current->source_id, current->target_id, buffer); + /* DEBUG("add edge %s:%s behavior %s\n", + current->source_id, current->target_id, buffer); */ if (cyberiada_decode_edge_behavior(buffer, &(current->behavior)) != CYBERIADA_NO_ERROR) { ERROR("cannot decode edge behavior\n"); return gpsInvalid; @@ -1350,13 +1600,13 @@ static GraphProcessorState handle_new_init_data(xmlNode* xml_node, strcmp(buffer, GRAPHML_CYB_GRAPH_FORMAT) == 0) { cyberiada_get_element_text(buffer, buffer_len, xml_node); - if (strcmp(buffer, CYBERIADA_VERSION_CYBERIADAML) == 0) { - cyberiada_copy_string(&(sm->version), &(sm->version_len), - CYBERIADA_VERSION_CYBERIADAML); - DEBUG("sm version %s\n", sm->version); + if (strcmp(buffer, CYBERIADA_FORMAT_CYBERIADAML) == 0) { + cyberiada_copy_string(&(sm->format), &(sm->format_len), + CYBERIADA_FORMAT_CYBERIADAML); + /* DEBUG("sm format %s\n", sm->format); */ return gpsInit; } else { - ERROR("Bad Cyberida-GraphML version: %s\n", buffer); + ERROR("Bad Cyberida-GraphML format: %s\n", buffer); } } else { ERROR("No graph version node\n"); @@ -1454,14 +1704,14 @@ static GraphProcessorState handle_node_data(xmlNode* xml_node, ERROR("Trying to set node %s label twice\n", current->id); return gpsInvalid; } - DEBUG("Set node %s title %s\n", current->id, buffer); + /* DEBUG("Set node %s title %s\n", current->id, buffer); */ cyberiada_copy_string(&(current->title), &(current->title_len), buffer); } else { if (current->behavior != NULL) { ERROR("Trying to set node %s behavior twice\n", current->id); return gpsInvalid; } - DEBUG("Set node %s behavior %s\n", current->id, buffer); + /* DEBUG("Set node %s behavior %s\n", current->id, buffer); */ if (cyberiada_decode_state_behavior(buffer, &(current->behavior)) != CYBERIADA_NO_ERROR) { ERROR("cannot decode node behavior\n"); return gpsInvalid; @@ -1501,7 +1751,7 @@ static GraphProcessorState handle_fake_node_data(xmlNode* xml_node, if (strcmp(buffer, GRAPHML_CYB_DATA_NAME) == 0 || strcmp(buffer, GRAPHML_CYB_DATA_DATA) == 0) { - int title = strcmp(buffer, GRAPHML_CYB_DATA_NAME) == 0; +/* int title = strcmp(buffer, GRAPHML_CYB_DATA_NAME) == 0; cyberiada_get_element_text(buffer, buffer_len, xml_node); if (title) { DEBUG("Set SM name %s\n", buffer); @@ -1509,7 +1759,7 @@ static GraphProcessorState handle_fake_node_data(xmlNode* xml_node, } else { DEBUG("Set SM info %s\n", buffer); cyberiada_copy_string(&(sm->info), &(sm->info_len), buffer); - } + }*/ } else { ERROR("bad fake node data key attribute %s\n", buffer); return gpsInvalid; @@ -1537,7 +1787,7 @@ static GraphProcessorState handle_edge_data(xmlNode* xml_node, } if (strcmp(buffer, GRAPHML_CYB_DATA_DATA) == 0) { cyberiada_get_element_text(buffer, buffer_len, xml_node); - DEBUG("Set edge %s behavior %s\n", current->id, buffer); + /* DEBUG("Set edge %s behavior %s\n", current->id, buffer); */ if (cyberiada_decode_edge_behavior(buffer, &(current->behavior)) != CYBERIADA_NO_ERROR) { ERROR("cannot decode edge behavior\n"); return gpsInvalid; @@ -1645,10 +1895,10 @@ static int cyberiada_build_graph(xmlNode* xml_root, /* } */ /* DEBUG("\n"); */ for (cur_xml_node = xml_root; cur_xml_node; cur_xml_node = cur_xml_node->next) { - DEBUG("xml node %s sm root %s gps %s\n", + /* DEBUG("xml node %s sm root %s gps %s\n", cur_xml_node->name, sm->nodes ? sm->nodes->id : "none", - debug_state_names[*gps]); + debug_state_names[*gps]); */ node_stack_push(stack); dispatch_processor(cur_xml_node, sm, stack, gps, processor_state_table, processor_state_table_size); @@ -1672,17 +1922,22 @@ static int cyberiada_decode_yed_xml(xmlNode* root, CyberiadaSM* sm) char buffer[MAX_STR_LEN]; size_t buffer_len = sizeof(buffer) - 1; GraphProcessorState gps = gpsInit; + CyberiadaNode* node = NULL; NodeStack* stack = NULL; + char berloga_format = 0; int res; if (cyberiada_get_attr_value(buffer, buffer_len, root, GRAPHML_BERLOGA_SCHEMENAME_ATTR) == CYBERIADA_NO_ERROR) { - cyberiada_copy_string(&(sm->name), &(sm->name_len), buffer); + cyberiada_copy_string(&(sm->format), &(sm->format_len), CYBERIADA_FORMAT_BERLOGA); + berloga_format = 1; + } else { + cyberiada_copy_string(&(sm->format), &(sm->format_len), CYBERIADA_FORMAT_OSTRANNA); + berloga_format = 0; } - cyberiada_copy_string(&(sm->version), &(sm->version_len), CYBERIADA_VERSION_BERLOGA); - DEBUG("sm version %s\n", sm->version); - + /* DEBUG("sm format %s\n", sm->format); */ + if ((res = cyberiada_build_graph(root, sm, &stack, &gps, yed_processor_state_table, yed_processor_state_table_size)) != CYBERIADA_NO_ERROR) { @@ -1693,15 +1948,18 @@ static int cyberiada_decode_yed_xml(xmlNode* root, CyberiadaSM* sm) ERROR("error with node stack\n"); return CYBERIADA_FORMAT_ERROR; } - - if (sm->name == NULL) { - if (sm->nodes && sm->nodes->children) { - cyberiada_copy_string(&(sm->name), &(sm->name_len), sm->nodes->children->title); - } else { - cyberiada_copy_string(&(sm->name), &(sm->name_len), ""); + + if (berloga_format) { + cyberiada_add_default_meta(sm, buffer); + } else { + if (sm->nodes) { + node = cyberiada_graph_find_node_by_type(sm->nodes, cybNodeCompositeState); + } + if (node) { + cyberiada_add_default_meta(sm, node->title); + } else { + cyberiada_add_default_meta(sm, EMPTY_TITLE); } - free(sm->version); - cyberiada_copy_string(&(sm->version), &(sm->version_len), CYBERIADA_VERSION_OSTRANNA); } return CYBERIADA_NO_ERROR; @@ -1726,7 +1984,7 @@ static int cyberiada_decode_cyberiada_xml(xmlNode* root, CyberiadaSM* sm) return CYBERIADA_FORMAT_ERROR; } - /*meta_node = cyberiada_graph_find_node(sm->nodes, ""); + /*meta_node = cyberiada_graph_find_node_by_id(sm->nodes, ""); if (meta_node == NULL) { ERROR("meta node not found\n"); @@ -1739,7 +1997,7 @@ static int cyberiada_decode_cyberiada_xml(xmlNode* root, CyberiadaSM* sm) edge = sm->edges; do { if (*(edge->source_id) == 0) { - ext_node = cyberiada_graph_find_node(sm->nodes, edge->target_id); + ext_node = cyberiada_graph_find_node_by_id(sm->nodes, edge->target_id); cyberiada_graph_add_extension(sm, ext_node->id, ext_node->title, ext_node->action); if (cyberiada_remove_node(sm, ext_node) != CYBERIADA_NO_ERROR) { ERROR("cannot remove ext node %s\n", edge->target_id); @@ -1835,7 +2093,7 @@ int cyberiada_read_sm(CyberiadaSM* sm, const char* filename, CyberiadaXMLFormat break; } - DEBUG("reading format %d\n", format); + /* DEBUG("reading format %d\n", format); */ if (format == cybxmlYED) { res = cyberiada_decode_yed_xml(root, sm); } else if (format == cybxmlCyberiada) { @@ -1865,6 +2123,30 @@ int cyberiada_read_sm(CyberiadaSM* sm, const char* filename, CyberiadaXMLFormat return res; } +static int cyberiada_print_meta(CyberiadaMetainformation* meta) +{ + size_t i; + char* value; + printf("Meta information:\n"); + + if (meta) { + for (i = 0; i < sizeof(cyberiada_metadata) / sizeof(MetainfoDeclaration); i++) { + value = *((char**)((char*)meta + cyberiada_metadata[i].value_offset)); + if (value) { + printf(" %s: %s\n", cyberiada_metadata[i].title, value); + } + } + + if (meta->actions_order_flag) { + printf(" actions order flag: %d\n", meta->actions_order_flag); + } + if (meta->event_propagation_flag) { + printf(" event propagation flag: %d\n", meta->event_propagation_flag); + } + } + return CYBERIADA_NO_ERROR; +} + static int cyberiada_print_behavior(CyberiadaBehavior* behavior, int level) { char levelspace[16]; @@ -1986,9 +2268,9 @@ int cyberiada_print_sm(CyberiadaSM* sm) CyberiadaEdge* cur_edge; /*CyberiadaExtension* cur_ext;*/ - printf("\nState Machine {name: %s, version: %s}\n", sm->name, sm->version); - printf(" Info: %s\n", sm->info); - + printf("\nState Machine [%s]\n", sm->format); + cyberiada_print_meta(sm->meta_info); + printf("Nodes:\n"); for (cur_node = sm->nodes; cur_node; cur_node = cur_node->next) { cyberiada_print_node(cur_node, 0); @@ -2011,3 +2293,625 @@ int cyberiada_print_sm(CyberiadaSM* sm) return CYBERIADA_NO_ERROR; } + +#define INDENT_STR " " +#define XML_WRITE_OPEN_E(w, e) if ((res = xmlTextWriterStartElement(w, (const xmlChar *)e)) < 0) { \ + fprintf(stderr, "xml open element error %d at %s:%d", res, __FILE__, __LINE__); \ + return CYBERIADA_XML_ERROR; \ + } +#define XML_WRITE_OPEN_E_I(w, e, indent) xmlTextWriterWriteRaw(w, (const xmlChar *)"\n"); \ + for (size_t _i = 0; _i < indent; _i++) { \ + xmlTextWriterWriteRaw(w, (const xmlChar *)INDENT_STR); \ + } \ + if ((res = xmlTextWriterStartElement(w, (const xmlChar *)e)) < 0) { \ + fprintf(stderr, "xml open element error %d at %s:%d", res, __FILE__, __LINE__); \ + return CYBERIADA_XML_ERROR; \ + } +#define XML_WRITE_OPEN_E_NS_I(w, e, ns, indent) xmlTextWriterWriteRaw(w, (const xmlChar *)"\n"); \ + for (size_t _i = 0; _i < indent; _i++) { \ + xmlTextWriterWriteRaw(w, (const xmlChar *)INDENT_STR); \ + } \ + if ((res = xmlTextWriterStartElementNS(w, (const xmlChar *)ns, \ + (const xmlChar *)e, NULL)) < 0) { \ + fprintf(stderr, "xml open element error %d at %s:%d", res, __FILE__, __LINE__); \ + return CYBERIADA_XML_ERROR; \ + } + +#define XML_WRITE_ATTR(w, a, v) if ((res = xmlTextWriterWriteAttribute(w, (const xmlChar *)a, \ + (const xmlChar *)v)) < 0) { \ + fprintf(stderr, "xml attribute error %d at %s:%d", res, __FILE__, __LINE__); \ + return CYBERIADA_XML_ERROR; \ + } + +#define XML_WRITE_TEXT(w, txt) if ((res = xmlTextWriterWriteString(w, (const xmlChar *)txt)) < 0) { \ + fprintf(stderr, "xml text error %d at %s:%d", res, __FILE__, __LINE__); \ + return CYBERIADA_XML_ERROR; \ + } + +#define XML_WRITE_CLOSE_E(w) if ((res = xmlTextWriterEndElement(w)) < 0) { \ + fprintf(stderr, "xml close element error %d at %s:%d", res, __FILE__, __LINE__); \ + return CYBERIADA_XML_ERROR; \ + } +#define XML_WRITE_CLOSE_E_I(w, indent) xmlTextWriterWriteRaw(w, (const xmlChar *)"\n"); \ + for (size_t _i = 0; _i < indent; _i++) { \ + xmlTextWriterWriteRaw(w, (const xmlChar *)INDENT_STR); \ + } \ + if ((res = xmlTextWriterEndElement(w)) < 0) { \ + fprintf(stderr, "xml close element error %d at %s:%d", res, __FILE__, __LINE__); \ + return CYBERIADA_XML_ERROR; \ + } + +typedef struct { + const char* attr_id; + const char* attr_for; + const char* attr_name; + const char* attr_type; + const char* attr_yfiles; +} GraphMLKey; + +static int cyberiada_write_sm_cyberiada(CyberiadaSM* sm, xmlTextWriterPtr writer) +{ + return CYBERIADA_NO_ERROR; +} + +#define GRAPHML_YED_NS "y" +#define GRAPHML_YED_ROOT_GRAPH_ID "G" + +static const char* yed_graphml_attributes[] = { + "xmlns", "http://graphml.graphdrawing.org/xmlns", + "xmlns:java", "http://www.yworks.com/xml/yfiles-common/1.0/java", + "xmlns:sys", "http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0", + "xmlns:x", "http://www.yworks.com/xml/yfiles-common/markup/2.0", + "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance", + "xmlns:y", "http://www.yworks.com/xml/graphml", + "xmlns:yed", "http://www.yworks.com/xml/yed/3", + "yed:schemaLocation", "http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd" +}; +const size_t yed_graphml_attributes_count = (sizeof(yed_graphml_attributes) / sizeof(const char*)); + +#define GRAPHML_YED_KEY_GRAPH_DESCR "d0" +#define GRAPHML_YED_KEY_PORT_GRAPHICS "d1" +#define GRAPHML_YED_KEY_PORT_GEOMETRY "d2" +#define GRAPHML_YED_KEY_PORT_USER_DATA "d3" +#define GRAPHML_YED_KEY_NODE_URL "d4" +#define GRAPHML_YED_KEY_NODE_DESCR "d5" +#define GRAPHML_YED_KEY_NODE_GRAPHICS "d6" +#define GRAPHML_YED_KEY_GRAPHML_RES "d7" +#define GRAPHML_YED_KEY_EDGE_URL "d8" +#define GRAPHML_YED_KEY_EDGE_DESCR "d9" +#define GRAPHML_YED_KEY_EDGE_GRAPHICS "d10" + +static int cyberiada_write_node_style_yed(xmlTextWriterPtr writer, CyberiadaNodeType type, size_t indent) +{ + int res; + + if (type == cybNodeCompositeState) { + XML_WRITE_OPEN_E_NS_I(writer, GRAPHML_YED_FILLNODE, GRAPHML_YED_NS, indent); + XML_WRITE_ATTR(writer, "color", "#E8EEF7"); + XML_WRITE_ATTR(writer, "color2", "#B7C9E3"); + XML_WRITE_ATTR(writer, "transparent", "false"); + XML_WRITE_CLOSE_E(writer); + } else if (type == cybNodeInitial) { + XML_WRITE_OPEN_E_NS_I(writer, GRAPHML_YED_FILLNODE, GRAPHML_YED_NS, indent); + XML_WRITE_ATTR(writer, "color", "#333333"); + XML_WRITE_ATTR(writer, "color2", "#000000"); + XML_WRITE_ATTR(writer, "transparent", "false"); + XML_WRITE_CLOSE_E(writer); + } else if (type == cybNodeCompositeState) { + XML_WRITE_OPEN_E_NS_I(writer, GRAPHML_YED_FILLNODE, GRAPHML_YED_NS, indent); + XML_WRITE_ATTR(writer, "color", "#F5F5F5"); + XML_WRITE_ATTR(writer, "transparent", "false"); + XML_WRITE_CLOSE_E(writer); + } + + XML_WRITE_OPEN_E_NS_I(writer, GRAPHML_YED_BORDERSTYLENODE, GRAPHML_YED_NS, indent); + XML_WRITE_ATTR(writer, "color", "#000000"); + XML_WRITE_ATTR(writer, "type", "line"); + XML_WRITE_ATTR(writer, "width", "1.0"); + XML_WRITE_CLOSE_E(writer); + + return CYBERIADA_NO_ERROR; +} + +static int cyberiada_write_node_title_yed(xmlTextWriterPtr writer, const char* title, size_t indent) +{ + int res; + + XML_WRITE_OPEN_E_NS_I(writer, GRAPHML_YED_LABELNODE, GRAPHML_YED_NS, indent); + XML_WRITE_ATTR(writer, "alignment", "center"); + XML_WRITE_ATTR(writer, "backgroundColor", "#EBEBEB"); + XML_WRITE_ATTR(writer, "fontSize", "15"); + XML_WRITE_ATTR(writer, "fontStyle", "bold"); + XML_WRITE_ATTR(writer, "textColor", "#000000"); + XML_WRITE_ATTR(writer, "xml:space", "preserve"); + XML_WRITE_ATTR(writer, "hasLineColor", "false"); + XML_WRITE_ATTR(writer, "visible", "true"); + XML_WRITE_ATTR(writer, "horizontalTextPosition", "center"); + XML_WRITE_ATTR(writer, "verticalTextPosition", "top"); + XML_WRITE_ATTR(writer, "autoSizePolicy", "node_width"); + XML_WRITE_ATTR(writer, "y", "0"); + XML_WRITE_ATTR(writer, "height", "20"); + XML_WRITE_ATTR(writer, "configuration", "com.yworks.entityRelationship.label.name"); + XML_WRITE_ATTR(writer, "modelName", "internal"); + XML_WRITE_ATTR(writer, "modelPosition", "t"); + + if (*title) { + XML_WRITE_TEXT(writer, title); + } + XML_WRITE_CLOSE_E(writer); + + return CYBERIADA_NO_ERROR; +} + +static int cyberiada_write_behavior_text(xmlTextWriterPtr writer, CyberiadaBehavior* behavior) +{ + int res; + char buffer[MAX_STR_LEN]; + size_t buffer_len = sizeof(buffer) - 1; + + while (behavior) { + + if (*(behavior->trigger) || *(behavior->action) || *(behavior->guard)) { + if (*(behavior->trigger)) { + if (behavior->type != cybBehaviorTransition) { + snprintf(buffer, buffer_len, "%s/", behavior->trigger); + } else { + if (*(behavior->guard)) { + snprintf(buffer, buffer_len, "%s [%s]/", behavior->trigger, behavior->guard); + } else { + snprintf(buffer, buffer_len, "%s/", behavior->trigger); + } + } + XML_WRITE_TEXT(writer, buffer); + if (behavior->next || *(behavior->action)) { + XML_WRITE_TEXT(writer, "\n"); + } + } else { + if (*(behavior->action)) { + XML_WRITE_TEXT(writer, "/\n"); + } else if (behavior->next) { + XML_WRITE_TEXT(writer, "\n"); + } + } + + if (*(behavior->action)) { + XML_WRITE_TEXT(writer, behavior->action); + XML_WRITE_TEXT(writer, "\n"); + } + + if (behavior->next) { + XML_WRITE_TEXT(writer, "\n"); + } + } + + behavior = behavior->next; + } + + return CYBERIADA_NO_ERROR; +} + +static int cyberiada_write_node_behavior_yed(xmlTextWriterPtr writer, CyberiadaBehavior* behavior, size_t indent) +{ + int res; + + XML_WRITE_OPEN_E_NS_I(writer, GRAPHML_YED_LABELNODE, GRAPHML_YED_NS, indent); + XML_WRITE_ATTR(writer, "alignment", "left"); + XML_WRITE_ATTR(writer, "hasBackgroundColor", "false"); + XML_WRITE_ATTR(writer, "fontSize", "12"); + XML_WRITE_ATTR(writer, "fontStyle", "plain"); + XML_WRITE_ATTR(writer, "textColor", "#000000"); + XML_WRITE_ATTR(writer, "xml:space", "preserve"); + XML_WRITE_ATTR(writer, "hasLineColor", "false"); + XML_WRITE_ATTR(writer, "visible", "true"); + XML_WRITE_ATTR(writer, "horizontalTextPosition", "center"); + XML_WRITE_ATTR(writer, "verticalTextPosition", "bottom"); + XML_WRITE_ATTR(writer, "autoSizePolicy", "node_size"); + XML_WRITE_TEXT(writer, "\n"); + XML_WRITE_TEXT(writer, "\n"); + + if (cyberiada_write_behavior_text(writer, behavior) != CYBERIADA_NO_ERROR) { + ERROR("error while writing node bevavior text\n"); + return CYBERIADA_XML_ERROR; + } + + XML_WRITE_CLOSE_E(writer); + + return CYBERIADA_NO_ERROR; +} + +static int cyberiada_write_edge_behavior_yed(xmlTextWriterPtr writer, CyberiadaBehavior* behavior, size_t indent) +{ + int res; + + XML_WRITE_OPEN_E_NS_I(writer, GRAPHML_YED_EDGELABEL, GRAPHML_YED_NS, indent); + XML_WRITE_ATTR(writer, "alignment", "center"); + XML_WRITE_ATTR(writer, "fontSize", "12"); + XML_WRITE_ATTR(writer, "fontStyle", "plain"); + XML_WRITE_ATTR(writer, "textColor", "#000000"); + XML_WRITE_ATTR(writer, "backgroundColor", "#F5F5F5"); + XML_WRITE_ATTR(writer, "configuration", "AutoFlippingLabel"); + XML_WRITE_ATTR(writer, "distance", "2.0"); + XML_WRITE_ATTR(writer, "hasLineColor", "false"); + XML_WRITE_ATTR(writer, "visible", "true"); + XML_WRITE_ATTR(writer, "xml:space", "preserve"); + XML_WRITE_ATTR(writer, "modelName", "centered"); + XML_WRITE_ATTR(writer, "modelPosition", "center"); + XML_WRITE_ATTR(writer, "preferredPlacement", "center_on_edge"); + + if (cyberiada_write_behavior_text(writer, behavior) != CYBERIADA_NO_ERROR) { + ERROR("error while writing node bevavior text\n"); + return CYBERIADA_XML_ERROR; + } + + /* +*/ + + XML_WRITE_CLOSE_E(writer); + + return CYBERIADA_NO_ERROR; +} + +static int cyberiada_write_geometry_yed(xmlTextWriterPtr writer, CyberiadaRect* rect, size_t indent) +{ + int res; + char buffer[MAX_STR_LEN]; + size_t buffer_len = sizeof(buffer) - 1; + + XML_WRITE_OPEN_E_NS_I(writer, GRAPHML_YED_GEOMETRYNODE, GRAPHML_YED_NS, indent); + snprintf(buffer, buffer_len, "%lf", rect->x); + XML_WRITE_ATTR(writer, GRAPHML_GEOM_X_ATTRIBUTE, buffer); + snprintf(buffer, buffer_len, "%lf", rect->y); + XML_WRITE_ATTR(writer, GRAPHML_GEOM_Y_ATTRIBUTE, buffer); + snprintf(buffer, buffer_len, "%lf", rect->width); + XML_WRITE_ATTR(writer, GRAPHML_GEOM_WIDTH_ATTRIBUTE, buffer); + snprintf(buffer, buffer_len, "%lf", rect->height); + XML_WRITE_ATTR(writer, GRAPHML_GEOM_HEIGHT_ATTRIBUTE, buffer); + XML_WRITE_CLOSE_E(writer); + + return CYBERIADA_NO_ERROR; +} + +static int cyberiada_write_node_yed(xmlTextWriterPtr writer, CyberiadaNode* node, size_t indent) +{ + int res; + CyberiadaNode* cur_node; + const char* text; + char buffer[MAX_STR_LEN]; + size_t buffer_len = sizeof(buffer) - 1; + + if (node->type == cybNodeSM) { + + for (cur_node = node->children; cur_node; cur_node = cur_node->next) { + res = cyberiada_write_node_yed(writer, cur_node, indent); + if (res != CYBERIADA_NO_ERROR) { + ERROR("error while writing root node %s\n", cur_node->id); + return CYBERIADA_XML_ERROR; + } + } + + } else { + + XML_WRITE_OPEN_E_I(writer, GRAPHML_NODE_ELEMENT, indent); + XML_WRITE_ATTR(writer, GRAPHML_ID_ATTRIBUTE, node->id); + + if (node->type == cybNodeInitial) { + + XML_WRITE_OPEN_E_I(writer, GRAPHML_DATA_ELEMENT, indent + 1); + XML_WRITE_ATTR(writer, GRAPHML_KEY_ATTRIBUTE, GRAPHML_YED_KEY_NODE_GRAPHICS); + + XML_WRITE_OPEN_E_NS_I(writer, GRAPHML_YED_GENERICNODE, GRAPHML_YED_NS, indent + 2); + XML_WRITE_ATTR(writer, "configuration", GRAPHML_YED_NODE_CONFIG_START2); + + if (node->geometry_rect) { + if (cyberiada_write_geometry_yed(writer, node->geometry_rect, indent + 3) != CYBERIADA_NO_ERROR) { + ERROR("error while writing initial node geometry\n"); + return CYBERIADA_XML_ERROR; + } + if (cyberiada_write_node_style_yed(writer, node->type, indent + 3) != CYBERIADA_NO_ERROR) { + ERROR("error while writing initial node style\n"); + return CYBERIADA_XML_ERROR; + } + } + + if (node->title) { + if (cyberiada_write_node_title_yed(writer, node->title, indent + 3) != CYBERIADA_NO_ERROR) { + ERROR("error while writing initial node label\n"); + return CYBERIADA_XML_ERROR; + } + } + + XML_WRITE_CLOSE_E_I(writer, indent + 2); + XML_WRITE_CLOSE_E_I(writer, indent + 1); + + } else if (node->type == cybNodeSimpleState) { + + XML_WRITE_OPEN_E_I(writer, GRAPHML_DATA_ELEMENT, indent + 1); + XML_WRITE_ATTR(writer, GRAPHML_KEY_ATTRIBUTE, GRAPHML_YED_KEY_NODE_GRAPHICS); + XML_WRITE_OPEN_E_NS_I(writer, GRAPHML_YED_GENERICNODE, GRAPHML_YED_NS, indent + 2); + + if (node->geometry_rect) { + if (cyberiada_write_geometry_yed(writer, node->geometry_rect, indent + 3) != CYBERIADA_NO_ERROR) { + ERROR("error while writing composite node geometry\n"); + return CYBERIADA_XML_ERROR; + } + if (cyberiada_write_node_style_yed(writer, node->type, indent + 3) != CYBERIADA_NO_ERROR) { + ERROR("error while writing composite node style\n"); + return CYBERIADA_XML_ERROR; + } + } + + text = node->title; + if (!text) text = ""; + if (cyberiada_write_node_title_yed(writer, text, indent + 3) != CYBERIADA_NO_ERROR) { + ERROR("error while writing simple node label\n"); + return CYBERIADA_XML_ERROR; + } + + if (cyberiada_write_node_behavior_yed(writer, node->behavior, indent + 3) != CYBERIADA_NO_ERROR) { + ERROR("error while writing simple node behavior\n"); + return CYBERIADA_XML_ERROR; + } + + XML_WRITE_CLOSE_E_I(writer, indent + 2); + XML_WRITE_CLOSE_E_I(writer, indent + 1); + + } else if (node->type == cybNodeCompositeState) { + XML_WRITE_ATTR(writer, "yfiles.foldertype", "group"); + + XML_WRITE_OPEN_E_I(writer, GRAPHML_DATA_ELEMENT, indent + 1); + XML_WRITE_ATTR(writer, GRAPHML_KEY_ATTRIBUTE, GRAPHML_YED_KEY_NODE_DESCR); + XML_WRITE_ATTR(writer, "xml:space", "preserve"); + XML_WRITE_CLOSE_E(writer); + + XML_WRITE_OPEN_E_I(writer, GRAPHML_DATA_ELEMENT, indent + 1); + XML_WRITE_ATTR(writer, GRAPHML_KEY_ATTRIBUTE, GRAPHML_YED_KEY_NODE_GRAPHICS); + XML_WRITE_OPEN_E_NS_I(writer, "ProxyAutoBoundsNode", GRAPHML_YED_NS, indent + 2); + XML_WRITE_OPEN_E_NS_I(writer, "Realizers", GRAPHML_YED_NS, indent + 3); + XML_WRITE_ATTR(writer, "active", "0"); + XML_WRITE_OPEN_E_NS_I(writer, GRAPHML_YED_GROUPNODE, GRAPHML_YED_NS, indent + 4); + + if (node->geometry_rect) { + if (cyberiada_write_geometry_yed(writer, node->geometry_rect, indent + 5) != CYBERIADA_NO_ERROR) { + ERROR("error while writing composite node geometry\n"); + return CYBERIADA_XML_ERROR; + } + if (cyberiada_write_node_style_yed(writer, node->type, indent + 5) != CYBERIADA_NO_ERROR) { + ERROR("error while writing composite node style\n"); + return CYBERIADA_XML_ERROR; + } + } + + text = node->title; + if (!text) text = ""; + if (cyberiada_write_node_title_yed(writer, text, indent + 5) != CYBERIADA_NO_ERROR) { + ERROR("error while writing composite node label\n"); + return CYBERIADA_XML_ERROR; + } + + if (cyberiada_write_node_behavior_yed(writer, node->behavior, indent + 5) != CYBERIADA_NO_ERROR) { + ERROR("error while writing composite node behavior\n"); + return CYBERIADA_XML_ERROR; + } + + XML_WRITE_OPEN_E_NS_I(writer, "Shape", GRAPHML_YED_NS, indent + 5); + XML_WRITE_ATTR(writer, "type", "roundrectangle"); + XML_WRITE_CLOSE_E(writer); + + XML_WRITE_CLOSE_E_I(writer, indent + 4); + XML_WRITE_CLOSE_E_I(writer, indent + 3); + XML_WRITE_CLOSE_E_I(writer, indent + 2); + XML_WRITE_CLOSE_E_I(writer, indent + 1); + + /* the root graph element */ + XML_WRITE_OPEN_E_I(writer, GRAPHML_GRAPH_ELEMENT, indent + 1); + snprintf(buffer, buffer_len, "%s:", node->id); + XML_WRITE_ATTR(writer, GRAPHML_ID_ATTRIBUTE, buffer); + XML_WRITE_ATTR(writer, GRAPHML_EDGEDEFAULT_ATTRIBUTE, GRAPHML_EDGEDEFAULT_ATTRIBUTE_VALUE); + + for (cur_node = node->children; cur_node; cur_node = cur_node->next) { + res = cyberiada_write_node_yed(writer, cur_node, indent + 2); + if (res != CYBERIADA_NO_ERROR) { + ERROR("error while writing node %s\n", cur_node->id); + return CYBERIADA_XML_ERROR; + } + } + + XML_WRITE_CLOSE_E_I(writer, indent + 1); + } + + XML_WRITE_CLOSE_E_I(writer, indent); + } + + return CYBERIADA_NO_ERROR; +} + +static int cyberiada_write_edge_yed(xmlTextWriterPtr writer, CyberiadaEdge* edge, size_t indent) +{ + int res; + char buffer[MAX_STR_LEN]; + size_t buffer_len = sizeof(buffer) - 1; + + XML_WRITE_OPEN_E_I(writer, GRAPHML_EDGE_ELEMENT, indent); + XML_WRITE_ATTR(writer, GRAPHML_SOURCE_ATTRIBUTE, edge->source_id); + XML_WRITE_ATTR(writer, GRAPHML_TARGET_ATTRIBUTE, edge->target_id); + + XML_WRITE_OPEN_E_I(writer, GRAPHML_DATA_ELEMENT, indent + 1); + XML_WRITE_ATTR(writer, GRAPHML_KEY_ATTRIBUTE, GRAPHML_YED_KEY_EDGE_GRAPHICS); + + XML_WRITE_OPEN_E_I(writer, GRAPHML_YED_POLYLINEEDGE, indent + 2); + + XML_WRITE_OPEN_E_I(writer, GRAPHML_YED_PATHNODE, indent + 3); + if (edge->geometry_source_point && edge->geometry_target_point) { + snprintf(buffer, buffer_len, "%lf", edge->geometry_source_point->x); + XML_WRITE_ATTR(writer, GRAPHML_YED_GEOM_SOURCE_X_ATTRIBUTE, buffer); + snprintf(buffer, buffer_len, "%lf", edge->geometry_source_point->y); + XML_WRITE_ATTR(writer, GRAPHML_YED_GEOM_SOURCE_Y_ATTRIBUTE, buffer); + snprintf(buffer, buffer_len, "%lf", edge->geometry_target_point->x); + XML_WRITE_ATTR(writer, GRAPHML_YED_GEOM_TARGET_X_ATTRIBUTE, buffer); + snprintf(buffer, buffer_len, "%lf", edge->geometry_target_point->y); + XML_WRITE_ATTR(writer, GRAPHML_YED_GEOM_TARGET_Y_ATTRIBUTE, buffer); + } else { + XML_WRITE_ATTR(writer, GRAPHML_YED_GEOM_SOURCE_X_ATTRIBUTE, "0"); + XML_WRITE_ATTR(writer, GRAPHML_YED_GEOM_SOURCE_Y_ATTRIBUTE, "0"); + XML_WRITE_ATTR(writer, GRAPHML_YED_GEOM_TARGET_X_ATTRIBUTE, "0"); + XML_WRITE_ATTR(writer, GRAPHML_YED_GEOM_TARGET_Y_ATTRIBUTE, "0"); + } + XML_WRITE_CLOSE_E(writer); + + XML_WRITE_OPEN_E_NS_I(writer, GRAPHML_YED_LINESTYLENODE, GRAPHML_YED_NS, indent + 3); + XML_WRITE_ATTR(writer, "color", "#000000"); + XML_WRITE_ATTR(writer, "type", "line"); + XML_WRITE_ATTR(writer, "width", "1.0"); + XML_WRITE_CLOSE_E(writer); + + XML_WRITE_OPEN_E_NS_I(writer, "Arrows", GRAPHML_YED_NS, indent + 3); + XML_WRITE_ATTR(writer, "source", "none"); + XML_WRITE_ATTR(writer, "target", "standard"); + XML_WRITE_CLOSE_E(writer); + + if (cyberiada_write_edge_behavior_yed(writer, edge->behavior, indent + 3) != CYBERIADA_NO_ERROR) { + ERROR("error while writing edge behavior\n"); + return CYBERIADA_XML_ERROR; + } + + XML_WRITE_CLOSE_E_I(writer, indent + 2); + XML_WRITE_CLOSE_E_I(writer, indent + 1); + XML_WRITE_CLOSE_E_I(writer, indent); + + return CYBERIADA_NO_ERROR; +} + +static GraphMLKey yed_graphml_keys[] = { + { GRAPHML_YED_KEY_GRAPH_DESCR, GRAPHML_GRAPH_ELEMENT, "description", "string", NULL }, + { GRAPHML_YED_KEY_PORT_GRAPHICS, GRAPHML_PORT_ATTRIBUTE, NULL, NULL, "portgraphics" }, + { GRAPHML_YED_KEY_PORT_GEOMETRY, GRAPHML_PORT_ATTRIBUTE, NULL, NULL, "portgeometry" }, + { GRAPHML_YED_KEY_PORT_USER_DATA, GRAPHML_PORT_ATTRIBUTE, NULL, NULL, "portuserdata" }, + { GRAPHML_YED_KEY_NODE_URL, GRAPHML_NODE_ELEMENT, "url", "string", NULL }, + { GRAPHML_YED_KEY_NODE_DESCR, GRAPHML_NODE_ELEMENT, "description", "string", NULL }, + { GRAPHML_YED_KEY_NODE_GRAPHICS, GRAPHML_NODE_ELEMENT, NULL, NULL, "nodegraphics" }, + { GRAPHML_YED_KEY_GRAPHML_RES, GRAPHML_GRAPHML_ELEMENT, NULL, NULL, "resources" }, + { GRAPHML_YED_KEY_EDGE_URL, GRAPHML_EDGE_ELEMENT, "url", "string", NULL }, + { GRAPHML_YED_KEY_EDGE_DESCR, GRAPHML_EDGE_ELEMENT, "description", "string", NULL }, + { GRAPHML_YED_KEY_EDGE_GRAPHICS, GRAPHML_EDGE_ELEMENT, NULL, NULL, "edgegraphics" } +}; +const size_t yed_graphml_keys_count = sizeof(yed_graphml_keys) / sizeof(GraphMLKey); + +static int cyberiada_write_sm_yed(CyberiadaSM* sm, xmlTextWriterPtr writer) +{ + size_t i; + int res; + GraphMLKey* key; + CyberiadaNode* cur_node; + CyberiadaEdge* cur_edge; + + XML_WRITE_OPEN_E(writer, GRAPHML_GRAPHML_ELEMENT); + + /* add YED graphml attributes */ + for (i = 0; i < yed_graphml_attributes_count; i += 2) { + XML_WRITE_ATTR(writer, yed_graphml_attributes[i], yed_graphml_attributes[i + 1]); + } + /* add scheme name if it is available */ + if (sm->meta_info && sm->meta_info->name && *sm->meta_info->name) { + XML_WRITE_ATTR(writer, GRAPHML_BERLOGA_SCHEMENAME_ATTR, sm->meta_info->name); + } + + /* write graphml keys */ + for (i = 0; i < yed_graphml_keys_count; i++) { + key = yed_graphml_keys + i; + XML_WRITE_OPEN_E_I(writer, GRAPHML_KEY_ELEMENT, 1); + XML_WRITE_ATTR(writer, GRAPHML_ID_ATTRIBUTE, key->attr_id); + XML_WRITE_ATTR(writer, GRAPHML_FOR_ATTRIBUTE, key->attr_for); + if (key->attr_name && key->attr_type) { + XML_WRITE_ATTR(writer, GRAPHML_ATTR_NAME_ATTRIBUTE, key->attr_name); + XML_WRITE_ATTR(writer, GRAPHML_ATTR_TYPE_ATTRIBUTE, key->attr_type); + } + if (key->attr_yfiles) { + XML_WRITE_ATTR(writer, GRAPHML_YED_YFILES_TYPE_ATTR, key->attr_yfiles); + } + XML_WRITE_CLOSE_E(writer); + } + + /* the root graph element */ + XML_WRITE_OPEN_E_I(writer, GRAPHML_GRAPH_ELEMENT, 1); + XML_WRITE_ATTR(writer, GRAPHML_ID_ATTRIBUTE, GRAPHML_YED_ROOT_GRAPH_ID); + XML_WRITE_ATTR(writer, GRAPHML_EDGEDEFAULT_ATTRIBUTE, GRAPHML_EDGEDEFAULT_ATTRIBUTE_VALUE); + + XML_WRITE_OPEN_E_I(writer, GRAPHML_DATA_ELEMENT, 2); + XML_WRITE_ATTR(writer, GRAPHML_KEY_ATTRIBUTE, GRAPHML_YED_KEY_GRAPH_DESCR); + XML_WRITE_ATTR(writer, "xml:space", "preserve"); + XML_WRITE_CLOSE_E(writer); + + /* write nodes */ + for (cur_node = sm->nodes; cur_node; cur_node = cur_node->next) { + res = cyberiada_write_node_yed(writer, cur_node, 2); + if (res != CYBERIADA_NO_ERROR) { + ERROR("error while writing node %s\n", cur_node->id); + return CYBERIADA_XML_ERROR; + } + } + + /* write edges */ + for (cur_edge = sm->edges; cur_edge; cur_edge = cur_edge->next) { + res = cyberiada_write_edge_yed(writer, cur_edge, 2); + if (res != CYBERIADA_NO_ERROR) { + ERROR("error while writing edge %s\n", cur_edge->id); + return CYBERIADA_XML_ERROR; + } + } + + XML_WRITE_CLOSE_E_I(writer, 1); + XML_WRITE_CLOSE_E_I(writer, 0); + + return CYBERIADA_NO_ERROR; +} + +int cyberiada_write_sm(CyberiadaSM* sm, const char* filename, CyberiadaXMLFormat format) +{ + xmlTextWriterPtr writer; + int res; + + if (format != cybxmlYED) { + ERROR("unsupported SM format for write: %d\n", format); + return CYBERIADA_BAD_PARAMETER; + } + + if (!sm) { + ERROR("empty SM to write\n"); + return CYBERIADA_BAD_PARAMETER; + } + + writer = xmlNewTextWriterFilename(filename, 0); + if (!writer) { + ERROR("cannot open xml writter for file %s\n", filename); + return CYBERIADA_XML_ERROR; + } + + res = xmlTextWriterStartDocument(writer, NULL, GRAPHML_XML_ENCODING, NULL); + if (res < 0) { + ERROR("error writing xml start document: %d\n", res); + xmlFreeTextWriter(writer); + return CYBERIADA_XML_ERROR; + } + + if (format == cybxmlYED) { + res = cyberiada_write_sm_yed(sm, writer); + } else if (format == cybxmlCyberiada) { + res = cyberiada_write_sm_cyberiada(sm, writer); + } + if (res != CYBERIADA_NO_ERROR) { + ERROR("error writing xml %d\n", res); + xmlFreeTextWriter(writer); + return CYBERIADA_XML_ERROR; + } + + res = xmlTextWriterEndDocument(writer); + if (res < 0) { + ERROR("error writing xml end document: %d\n", res); + xmlFreeTextWriter(writer); + return CYBERIADA_XML_ERROR; + } + + xmlFreeTextWriter(writer); + + return CYBERIADA_NO_ERROR; +} diff --git a/cyberiadaml.h b/cyberiadaml.h index 751b003..78ea1d2 100644 --- a/cyberiadaml.h +++ b/cyberiadaml.h @@ -33,27 +33,26 @@ extern "C" { /* SM node types: */ typedef enum { - cybNodeSM = 0, /* state machine */ - cybNodeSimpleState, /* simple state */ - cybNodeCompositeState, /* composite state */ - cybNodeSubmachineState, /* submachine state */ - cybNodeComment, /* comment node */ - cybNodeInitial, /* initial pseudostate */ - cybNodeFinal, /* final pseudostate */ - cybNodeChoice, /* final pseudostate */ - cybNodeJunction, /* junction pseudostate */ - cybNodeEntry, /* entry pseudostate */ - cybNodeExit, /* exit pseudostate */ - cybNodeHistory, /* shallow history pseudostate */ - cybNodeDeepHistory, /* deep history pseudostate */ - cybNodeFork, /* fork pseudostate */ - cybNodeJoin, /* join pseudostate */ - cybNodeTerminate, /* terminate pseudostate */ + cybNodeSM = 0, /* state machine */ + cybNodeSimpleState = 1, /* simple state */ + cybNodeCompositeState = 2, /* composite state */ + cybNodeSubmachineState = 4, /* submachine state */ + cybNodeComment = 8, /* comment node */ + cybNodeInitial = 16, /* initial pseudostate */ + cybNodeFinal = 32, /* final pseudostate */ + cybNodeChoice = 64, /* final pseudostate */ + cybNodeEntry = 128, /* entry pseudostate */ + cybNodeExit = 256, /* exit pseudostate */ + cybNodeShallowHistory = 512, /* shallow history pseudostate */ + cybNodeTerminate = 1024, /* terminate pseudostate */ } CyberiadaNodeType; +typedef unsigned int CyberiadaNodeTypeMask; + /* SM node types: */ typedef enum { cybEdgeTransition = 0, + cybEdgeComment = 1, } CyberiadaEdgeType; /* SM behavior types: */ @@ -61,7 +60,7 @@ typedef enum { cybBehaviorTransition = 0, cybBehaviorEntry = 1, cybBehaviorExit = 2, - cybBehaviorDo = 3, + cybBehaviorDo = 4, } CyberiadaBehaviorType; /* SM node & transitions geometry */ @@ -126,34 +125,47 @@ typedef struct _CyberiadaEdge { struct _CyberiadaEdge* next; } CyberiadaEdge; -/* SM extentions -typedef struct _CyberiadaExtension { - char* id; - size_t id_len; - char* title; - size_t title_len; - char* data; - size_t data_len; - struct _CyberiadaExtension* next; - } CyberiadaExtension;*/ - +/* SM metainformation */ +typedef struct { + char* standard_version; /* HSM standard version (required parameter) */ + size_t standard_version_len; + char* platform_name; /* target platform name */ + size_t platform_name_len; + char* platform_version; /* target platform version */ + size_t platform_version_len; + char* platform_language; /* target platform language */ + size_t platform_language_len; + char* target_system; /* target system controlled by the SM */ + size_t target_system_len; + char* name; /* document name */ + size_t name_len; + char* author; /* document author */ + size_t author_len; + char* contact; /* document author's contact */ + size_t contact_len; + char* description; /* document description */ + size_t description_len; + char* version; /* document version */ + size_t version_len; + char* date; /* document date */ + size_t date_len; + char actions_order_flag; /* actions order flag (0 = not set, 1 = transition first, 2 = exit first) */ + char event_propagation_flag; /* event propagation flag (0 = not set, 1 = block events, 2 = propagate events) */ +} CyberiadaMetainformation; + /* SM graph (state machine) */ typedef struct { - char* name; - size_t name_len; - char* version; - size_t version_len; - char* info; - size_t info_len; + char* format; /* SM graph format string */ + size_t format_len; /* SM graph format string length */ + CyberiadaMetainformation* meta_info; CyberiadaNode* nodes; CyberiadaEdge* edges; -/* CyberiadaExtension* extensions;*/ } CyberiadaSM; /* SM GraphML supported formats */ typedef enum { - cybxmlYED = 0, - cybxmlCyberiada, + cybxmlCyberiada = 0, + cybxmlYED, cybxmlUnknown } CyberiadaXMLFormat; @@ -165,9 +177,10 @@ typedef enum { #define CYBERIADA_XML_ERROR 1 #define CYBERIADA_FORMAT_ERROR 2 #define CYBERIADA_BEHAVIOR_FORMAT_ERROR 3 -#define CYBERIADA_NOT_FOUND 4 -#define CYBERIADA_BAD_PARAMETER 5 -#define CYBERIADA_ASSERT 6 +#define CYBERIADA_METADATA_FORMAT_ERROR 4 +#define CYBERIADA_NOT_FOUND 5 +#define CYBERIADA_BAD_PARAMETER 6 +#define CYBERIADA_ASSERT 7 /* ----------------------------------------------------------------------------- * The Cyberiada GraphML library functions @@ -176,16 +189,19 @@ typedef enum { /* Allocate the SM structure in memory (for heap usage) */ CyberiadaSM* cyberiada_create_sm(void); - /* Initialize the SM structure. Do not use the structure before the initialization! */ + /* Initialize the SM structure. */ + /* Do not use the structure before the initialization! */ int cyberiada_init_sm(CyberiadaSM* sm); - /* Cleanup the content of the SM structure, free the conents memory */ + /* Cleanup the content of the SM structure */ + /* Free the allocated memory of the structure content but not the structure itself */ int cyberiada_cleanup_sm(CyberiadaSM* sm); - /* Free the allocated SM structure (for heap usage) */ + /* Free the allocated SM structure with the content (for heap usage) */ int cyberiada_destroy_sm(CyberiadaSM* sm); /* Read an XML file and decode the SM structure */ + /* Allocate the SM structure first */ int cyberiada_read_sm(CyberiadaSM* sm, const char* filename, CyberiadaXMLFormat format); /* Encode the SM structure and write the data to an XML file */ diff --git a/test.c b/test.c index 2f7064a..392e9c0 100644 --- a/test.c +++ b/test.c @@ -25,68 +25,168 @@ #include "cyberiadaml.h" const char* formats[] = { - "yed", /* cybxmlYED */ - "cyberiada" /* cybxmlCyberiada */ + "cyberiada", /* cybxmlCyberiada */ + "yed" /* cybxmlYED */ }; -const char* format_names[] = { - "yEd editor format used by Ostranna projects and the Orbita Simulator", - "Cyberiada-GraphML format" +const char* format_descr[] = { + "Cyberiada-GraphML format", + "yEd editor format used by Ostranna projects and the Orbita Simulator" }; -unsigned int format_count = sizeof(formats) / sizeof(char*); +size_t format_count = sizeof(formats) / sizeof(char*); +const char* commands[] = { + "print", + "convert" +}; + +const char* command_descr[] = { + "print HSM diagram content to stdout; use -f key to set the scheme format (default - unknown)", + "convert HSM from format to into the file named " +}; + + +size_t command_count = sizeof(commands) / sizeof(char*); + static void print_usage(const char* name) { unsigned int i; - fprintf(stderr, "%s [-t ] \n\n", name); + fprintf(stderr, "%s [-f ] [-t -o ] \n\n", name); + fprintf(stderr, "Supported commands:\n"); + for (i = 0; i < command_count; i++) { + fprintf(stderr, " %-20s %s\n", commands[i], command_descr[i]); + } fprintf(stderr, "Supported formats:\n"); for (i = 0; i < format_count; i++) { - fprintf(stderr, " %-20s %s\n", formats[i], format_names[i]); + fprintf(stderr, " %-20s %s\n", formats[i], format_descr[i]); } fprintf(stderr, "\n"); } int main(int argc, char** argv) { - char *filename; - char *format_str = ""; - CyberiadaXMLFormat format = cybxmlUnknown; + char *source_filename, *dest_filename; + char *source_format_str = "", *dest_format_str = ""; + CyberiadaXMLFormat source_format = cybxmlUnknown, dest_format = cybxmlUnknown; unsigned int i; int res; CyberiadaSM sm; + char print = 0; - if (argc == 4) { - if (strcmp(argv[1], "-t") != 0) { + if (argc < 3) { + print_usage(argv[0]); + return 1; + } + + if (argc == 5 || argc == 9) { + if (strcmp(argv[2], "-f") != 0) { print_usage(argv[0]); return 1; } for(i = 0; i < format_count; i++) { - if (strcmp(argv[2], formats[i]) == 0) { - format = (CyberiadaXMLFormat)i; - format_str = argv[2]; + if (strcmp(argv[3], formats[i]) == 0) { + source_format = (CyberiadaXMLFormat)i; + source_format_str = argv[3]; break; } } - if(strlen(format_str) == 0) { - fprintf(stderr, "unsupported graphml format %s\n", argv[2]); + if(strlen(source_format_str) == 0) { + fprintf(stderr, "unsupported graphml format %s\n", argv[3]); print_usage(argv[0]); return 2; } - filename = argv[3]; - } else if (argc == 2) { - filename = argv[1]; - } else { - print_usage(argv[0]); - return 1; } - if ((res = cyberiada_read_sm(&sm, filename, format)) != CYBERIADA_NO_ERROR) { + + if (strcmp(argv[1], commands[0]) == 0) { + /* print */ + + if (argc == 5) { + source_filename = argv[4]; + } else if (argc == 3) { + source_filename = argv[2]; + } else { + print_usage(argv[0]); + return 1; + } + + print = 1; + + } else if (strcmp(argv[1], commands[1]) == 0) { + /* convert */ + + if (argc < 7) { + print_usage(argv[0]); + return 1; + } + + if (argc == 9) { + if (strcmp(argv[4], "-t") != 0) { + print_usage(argv[0]); + return 1; + } + for(i = 0; i < format_count; i++) { + if (strcmp(argv[5], formats[i]) == 0) { + dest_format = (CyberiadaXMLFormat)i; + dest_format_str = argv[5]; + break; + } + } + if(strlen(dest_format_str) == 0) { + fprintf(stderr, "unsupported graphml format %s\n", argv[5]); + print_usage(argv[0]); + return 2; + } + if (strcmp(argv[6], "-o") != 0) { + print_usage(argv[0]); + return 1; + } + dest_filename = argv[7]; + source_filename = argv[8]; + } else if (argc == 7) { + if (strcmp(argv[2], "-t") != 0) { + print_usage(argv[0]); + return 1; + } + for(i = 0; i < format_count; i++) { + if (strcmp(argv[3], formats[i]) == 0) { + dest_format = (CyberiadaXMLFormat)i; + dest_format_str = argv[3]; + break; + } + } + if(strlen(dest_format_str) == 0) { + fprintf(stderr, "unsupported graphml format %s\n", argv[3]); + print_usage(argv[0]); + return 2; + } + if (strcmp(argv[4], "-o") != 0) { + print_usage(argv[0]); + return 1; + } + dest_filename = argv[5]; + source_filename = argv[6]; + } else { + print_usage(argv[0]); + return 1; + } + } + + if ((res = cyberiada_read_sm(&sm, source_filename, source_format)) != CYBERIADA_NO_ERROR) { fprintf(stderr, "error while reading %s file: %d\n", - filename, res); + source_filename, res); return 2; } - cyberiada_print_sm(&sm); + if (print) { + cyberiada_print_sm(&sm); + } else { + if ((res = cyberiada_write_sm(&sm, dest_filename, dest_format)) != CYBERIADA_NO_ERROR) { + fprintf(stderr, "error while writing %s file: %d\n", + dest_filename, res); + return 3; + } + } cyberiada_cleanup_sm(&sm);