/* * Gem-graph client * * Desc: Model parsing functions * * Copyright (C) 2023 Jean Sirmai * Copyright (C) 2024 Adrien Bourmault * * This file is part of Gem-graph. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include // http://xmlsoft.org/examples/#parse1.c // https://gnome.pages.gitlab.gnome.org/libxml2/devhelp/general.html #include "base.h" #define READ_SITE 1 << 0 #define READ_WEIGHT 1 << 1 #define READ_X 1 << 2 #define READ_Y 1 << 3 #define READ_Z 1 << 4 #define SUCCESSFUL_READ_ARROW_X (READ_SITE | READ_WEIGHT | READ_X) #define SUCCESSFUL_READ_ARROW_XY (READ_SITE | READ_WEIGHT | READ_X | READ_Y) #define SUCCESSFUL_READ_ARROW_XYZ (READ_SITE | READ_WEIGHT | READ_X | READ_Y | READ_Z) static xmlDocPtr model; static xmlHashTablePtr model_hashtable; bool model_init(const char *content, size_t length, const char *basename) { xmlNode *node; /* * this initialize the library and check potential ABI mismatches * between the version it was compiled for and the actual shared * library used. */ LIBXML_TEST_VERSION model = xmlReadMemory(content, length, basename, NULL, 0); if (model == NULL ) { return false; } node = xmlDocGetRootElement(model); if (node == NULL) { g_printerr("Empty XML model !\n"); xmlFreeDoc(model); return false; } if (xmlStrcmp(node->name, (xmlChar *) "gem-graph-model")) { g_printerr("document of the wrong type, root node != gem-graph-model\n"); xmlFreeDoc(model); return false; } model_hashtable = xmlHashCreate(0); if (model_hashtable == NULL) { g_printerr("Can't create model hash table !\n"); xmlFreeDoc(model); return false; } return true; } bool model_shutdown(void) { xmlFreeDoc(model); xmlHashFree(model_hashtable, NULL); // Cleanup function for the XML library xmlCleanupParser(); // This is to debug memory for regression tests xmlMemoryDump(); return true; } /******************************************************************************/ static inline xmlNodePtr getNextChild(xmlNodePtr node, xmlChar *last) { while (node != NULL && xmlStrcmp(node->name, last)) { // //printf(" <>--- line n°%lu <%s>\n", xmlGetLineNo(node), node->name); node = node->next; } return node; } static inline xmlChar* splitStrAtSlash(xmlChar *toSplit) { toSplit = (xmlChar *)xmlStrchr(toSplit, '/'); toSplit = xmlStrsub (toSplit, 1, xmlStrlen(toSplit)); return toSplit; } static inline xmlChar* getFirstTag(xmlChar *path) { xmlChar *preop = path; path = (xmlChar *)xmlStrchr(path, '/'); path = xmlStrsub (path, 1, xmlStrlen(path)); //printf("%s = %s + / + %s\n", preop, xmlStrsub (preop, 0, xmlStrlen(preop) - xmlStrlen(path) - 1), path); return xmlStrsub (preop, 0, xmlStrlen(preop) - xmlStrlen(path) - 1); } static inline xmlChar* getLastTag(xmlChar *path) { while ((ulong)xmlStrchr (path, '/')) path = splitStrAtSlash((xmlChar *)path); // //printf("last tag in the path = <%s>\n", path); return path; // which is no more the given path but only its last tag ! } /******************************************************************************/ static xmlNodePtr model_get_node(xmlChar *path) { xmlNodePtr node; xmlChar *extrait; xmlChar *reste, *last; // Lookup for node from path in hash table node = xmlHashLookup(model_hashtable, path); // Found a node in hash table if (node) { return node; // no node found in hash table } else { reste = path; last = getLastTag(reste); node = xmlDocGetRootElement(model); while (xmlStrchr (reste, '/')) { extrait = getFirstTag(reste); reste = splitStrAtSlash((xmlChar *)reste); node = node->xmlChildrenNode; node = getNextChild(node, extrait); } if(node && xmlStrcmp(node->name, last)) { node = node->xmlChildrenNode; while (node && xmlStrcmp(node->name, last)) { node = node->next; } xmlHashAddEntry (model_hashtable, path, node); } return node; } return NULL; } /******************************************************************************/ static inline long model_get_node_long_attrib(xmlNodePtr node, char *id) { xmlAttr *attribute; xmlChar* value; long ret_value; if (node && node->properties) { attribute = node->properties; while(attribute && attribute->name && attribute->children) { if (!xmlStrcmp(attribute->name, (const xmlChar *)id)) { value = xmlNodeListGetString(node->doc, attribute->children, 1); ret_value = strtol((char *)value, NULL, 0); xmlFree(value); return ret_value; } attribute = attribute->next; } } return 0; } static inline bool model_get_node_str_attrib(xmlNodePtr node, char *id, char *dest) { xmlAttr *attribute; xmlChar* value; if (node && node->properties) { attribute = node->properties; while(attribute && attribute->name && attribute->children) { if (!xmlStrcmp(attribute->name, (const xmlChar *)id)) { value = xmlNodeListGetString(node->doc, attribute->children, 1); strcpy(dest, (char *)value); xmlFree(value); return true; } attribute = attribute->next; } } return false; } /******************************************************************************/ char model_get_dim(void) { // xmlAttr *attribute; // xmlChar* value; xmlNodePtr node = model_get_node( (xmlChar *)"parameters/space-param/dimension"); if (xmlHasProp (node, (xmlChar *) "z")) return 3; if (xmlHasProp (node, (xmlChar *) "y")) return 2; if (xmlHasProp (node, (xmlChar *) "x")) return 1; return 0; } long model_get_dim_value(const char *axis) { // xmlAttr *attribute; // xmlChar *value; // long ret_value; xmlNodePtr node = model_get_node( (xmlChar *)"parameters/space-param/dimension"); return model_get_node_long_attrib(node, (char *)axis); } char model_get_multiplicity(void) { // xmlAttr *attribute; // xmlChar* value; xmlNodePtr node = model_get_node( (xmlChar *)"parameters/space-param/site_multiplicity"); if (node->children) if (node->children->content) return (char)strtol((char *)node->children->content, NULL, 0); return 0; } bool model_get_next_state(char *new_state_id) { static xmlNodePtr cur_node = NULL; // xmlAttr *attribute; // xmlChar *value; if (cur_node == NULL) { // Get first state cur_node = model_get_node((xmlChar *)"savedstates/state"); } else { // Get next state if (cur_node->next) cur_node = cur_node->next; else return false; } // Lookup in properties if (model_get_node_str_attrib(cur_node, "id", new_state_id)) return true; cur_node = NULL; return false; } long model_get_state_arrows_count(const char *state_id) { xmlNodePtr cur_node = NULL; // xmlAttr *attribute; long value = 0; bool found = false; char temp_char[25]; // uint check = 0; // bit field checker //printf("NEW CALL : cur_node = %p\n", cur_node); assert(state_id); // Get first state node cur_node = model_get_node((xmlChar *)"savedstates/state"); // Lookup in properties while (cur_node && cur_node->properties) { // attribute = cur_node->properties; < usage ? // Look for the id attribute if (model_get_node_str_attrib(cur_node, "id", (char *)&temp_char)) { if (!xmlStrcmp((const xmlChar *)temp_char, (const xmlChar *)state_id)) { found = true; break; } } cur_node = cur_node->next; } // Check if the state has been found if (!found) { cur_node = NULL; return -1; } // Count arrows if (cur_node->children) { cur_node = cur_node->children; while (cur_node) { if (!xmlStrcmp(cur_node->name, (const xmlChar *)"arrow")) value++; cur_node = cur_node->next; } } else { return -1; } return value; } bool model_get_next_arrow(struct arrow_t *new_arrow, const char *state_id, char dimension) { static xmlNodePtr cur_node = NULL; xmlAttr *attribute; xmlChar *value; bool found = false; char temp_char[25]; uint check = 0; // bit field checker //printf("NEW CALL : cur_node = %p\n", cur_node); assert(new_arrow); assert(state_id); if (cur_node == NULL) { // Get first state node cur_node = model_get_node((xmlChar *)"savedstates/state"); // Lookup in properties while (cur_node && cur_node->properties) { attribute = cur_node->properties; // Look for the id attribute if (model_get_node_str_attrib(cur_node, "id", (char *)&temp_char)) { if (!xmlStrcmp((xmlChar *)temp_char, (const xmlChar *)state_id)) { found = true; break; } } cur_node = cur_node->next; } // Check if the state has been found if (!found) { cur_node = NULL; return false; } // Get first arrow if (cur_node->children) { cur_node = cur_node->children; found = false; while (cur_node && cur_node->name) { if (!xmlStrcmp(cur_node->name, (const xmlChar *)"arrow")) { found = true; break; } cur_node = cur_node->next; } } // Check if the state has been found if (!found) { cur_node = NULL; return false; } } else { // Get next arrow found = false; while (cur_node->next) { cur_node = cur_node->next; if (!xmlStrcmp(cur_node->name, (const xmlChar *)"arrow")) { found = true; break; } } // Check if the state has been found if (!found) { cur_node = NULL; return false; } } //printf("DURING CALL : cur_node = %p\n", cur_node); //printf("DURING CALL : cur_node->name = %s\n", cur_node->name); // Lookup in properties if (cur_node && cur_node->properties) { attribute = cur_node->properties; while(attribute && attribute->name && attribute->children) { //printf("attr name : %s\n", attribute->name); if (!xmlStrcmp(attribute->name, (const xmlChar *)"site")) { value = xmlNodeListGetString(cur_node->doc, attribute->children, 1); new_arrow->site = strtol((char *)value, NULL, 0); xmlFree(value); check |= READ_SITE; } if (!xmlStrcmp(attribute->name, (const xmlChar *)"weight")) { value = xmlNodeListGetString(cur_node->doc, attribute->children, 1); new_arrow->load = strtol((char *)value, NULL, 0); xmlFree(value); check |= READ_WEIGHT; } if (!xmlStrcmp(attribute->name, (const xmlChar *)"x")) { value = xmlNodeListGetString(cur_node->doc, attribute->children, 1); new_arrow->x = strtol((char *)value, NULL, 0); xmlFree(value); check |= READ_X; } if (!xmlStrcmp(attribute->name, (const xmlChar *)"y")) { value = xmlNodeListGetString(cur_node->doc, attribute->children, 1); new_arrow->y = strtol((char *)value, NULL, 0); xmlFree(value); check |= READ_Y; } if (!xmlStrcmp(attribute->name, (const xmlChar *)"z")) { value = xmlNodeListGetString(cur_node->doc, attribute->children, 1); new_arrow->z = strtol((char *)value, NULL, 0); xmlFree(value); check |= READ_Z; } attribute = attribute->next; } switch(dimension) { case 3: return (bool)(check & SUCCESSFUL_READ_ARROW_XYZ); case 2: return (bool)(check & SUCCESSFUL_READ_ARROW_XY); case 1: return (bool)(check & SUCCESSFUL_READ_ARROW_X); } } cur_node = NULL; return false; }