GTK4_GG_hack/parsing.c

482 lines
14 KiB
C
Raw Normal View History

/*
* Gem-graph client
*
* Desc: Model parsing functions
*
* Copyright (C) 2023 Jean Sirmai <jean@a-lec.org>
* Copyright (C) 2024 Adrien Bourmault <neox@a-lec.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <limits.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlreader.h> // 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;
}