diff --git a/Makefile.am b/Makefile.am index 99dfb14..9e70d15 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,13 +21,15 @@ pkginclude_HEADERS = \ include/igloo/types.h \ include/igloo/typedef.h \ include/igloo/buffer.h \ - include/igloo/list.h + include/igloo/list.h \ + include/igloo/reportxml.h libigloo_la_SOURCES = \ src/libigloo.c \ src/ro.c \ src/buffer.c \ - src/list.c + src/list.c \ + src/reportxml.c libigloo_la_LIBADD = \ avl/libiceavl.la \ httpp/libicehttpp.la \ diff --git a/include/igloo/reportxml.h b/include/igloo/reportxml.h new file mode 100644 index 0000000..7ae8366 --- /dev/null +++ b/include/igloo/reportxml.h @@ -0,0 +1,148 @@ +/* Icecast + * + * This program is distributed under the GNU General Public License, version 2. + * A copy of this license is included with this source. + * + * Copyright 2018, Philipp "ph3-der-loewe" Schafft , + */ + +/* This file contains the API for report XML document parsing, manipulation, and rendering. */ + +#ifndef _LIBIGLOO__REPORTXML_H_ +#define _LIBIGLOO__REPORTXML_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +igloo_RO_FORWARD_TYPE(igloo_reportxml_t); +igloo_RO_FORWARD_TYPE(igloo_reportxml_node_t); +igloo_RO_FORWARD_TYPE(igloo_reportxml_database_t); + + +/* XML Tag Types + * While a hint of what the nodes are used for is given, see the specification for more details. + */ +typedef enum { + /* This is a virtual type used to indicate error conditions */ + REPORTXML_NODE_TYPE__ERROR, + /* is the root element of report XML documents */ + REPORTXML_NODE_TYPE_REPORT, + /* is used to define templates */ + REPORTXML_NODE_TYPE_DEFINITION, + /* defines an event that is reported */ + REPORTXML_NODE_TYPE_INCIDENT, + /* defines the state an resulted in */ + REPORTXML_NODE_TYPE_STATE, + /* provides helpful information about the location some event happend */ + REPORTXML_NODE_TYPE_BACKTRACE, + /* defines an element within */ + REPORTXML_NODE_TYPE_POSITION, + /* allows to skip s in for any reason + * (e.g. they are unknown or consider of no intrest) + */ + REPORTXML_NODE_TYPE_MORE, + /* provides a machine readable way to actually fix the problem */ + REPORTXML_NODE_TYPE_FIX, + /* defines a specific action to do */ + REPORTXML_NODE_TYPE_ACTION, + /* allows to define why an event happend */ + REPORTXML_NODE_TYPE_REASON, + /* is used to provide messages to the user. + * The content of is not machine readable. + */ + REPORTXML_NODE_TYPE_TEXT, + /* provides a way to present a point in time an event happend */ + REPORTXML_NODE_TYPE_TIMESTAMP, + /* names a resource that was involved in the event such as user input or the result */ + REPORTXML_NODE_TYPE_RESOURCE, + /* provides an actual value for a */ + REPORTXML_NODE_TYPE_VALUE, + /* provides a way to refer to external documents such as documentation */ + REPORTXML_NODE_TYPE_REFERENCE, + /* is used to allow application specific extensions */ + REPORTXML_NODE_TYPE_EXTENSION +} reportxml_node_type_t; + + +/* ---[ Document level ]--- */ +/* The document object is NOT thread safe. */ + + +/* This gets the root node of a report XML document */ +igloo_reportxml_node_t * reportxml_get_root_node(igloo_reportxml_t *report); +/* This selects a node by an attribute and it's value. + * This is mostly useful to look for an object by using it's ID. + * If more than one node matches the first one found is returned. + * If the parameter include_definitions is true nodes from within + * are also considered. If it is false nodes inside + * s are skipped. + */ +igloo_reportxml_node_t * reportxml_get_node_by_attribute(igloo_reportxml_t *report, const char *key, const char *value, int include_definitions); +/* This gets a node by it's type. Otherwise identical to reportxml_get_node_by_attribute() */ +igloo_reportxml_node_t * reportxml_get_node_by_type(igloo_reportxml_t *report, reportxml_node_type_t type, int include_definitions); +/* This function parses an XML document and returns the parst report XML document */ +igloo_reportxml_t * reportxml_parse_xmldoc(xmlDocPtr doc); +/* This function renders an report XML document as XML structure */ +xmlDocPtr reportxml_render_xmldoc(igloo_reportxml_t *report); + + +/* ---[ Node level ]--- */ +/* The node object is NOT thread safe. */ + + +/* This creates a new node of type type. + * It's id, definition, and akindof attributes can be given as parameters. + */ +igloo_reportxml_node_t * reportxml_node_new(reportxml_node_type_t type, const char *id, const char *definition, const char *akindof); +/* This parses an XML node and returns the resulting report XML node */ +igloo_reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode); +/* Copy an report XML node (and it's children) */ +igloo_reportxml_node_t * reportxml_node_copy(igloo_reportxml_node_t *node); +/* Renders an report XML node as XML node */ +xmlNodePtr reportxml_node_render_xmlnode(igloo_reportxml_node_t *node); +/* This gets the type of an report XML node */ +reportxml_node_type_t reportxml_node_get_type(igloo_reportxml_node_t *node); +/* Gets and Sets attribute values */ +int reportxml_node_set_attribute(igloo_reportxml_node_t *node, const char *key, const char *value); +char * reportxml_node_get_attribute(igloo_reportxml_node_t *node, const char *key); +/* Adds, counts, and get child nodes */ +int reportxml_node_add_child(igloo_reportxml_node_t *node, igloo_reportxml_node_t *child); +ssize_t reportxml_node_count_child(igloo_reportxml_node_t *node); +igloo_reportxml_node_t * reportxml_node_get_child(igloo_reportxml_node_t *node, size_t idx); +/* This gets an child by it's value of the given attribute. See reportxml_get_node_by_attribute() for more details. */ +igloo_reportxml_node_t * reportxml_node_get_child_by_attribute(igloo_reportxml_node_t *node, const char *key, const char *value, int include_definitions); +/* This gets a child by it's type. Otherwise identical to reportxml_node_get_child_by_attribute() */ +igloo_reportxml_node_t * reportxml_node_get_child_by_type(igloo_reportxml_node_t *node, reportxml_node_type_t type, int include_definitions); +/* This gets and sets the text content of an node (used for ) */ +int reportxml_node_set_content(igloo_reportxml_node_t *node, const char *value); +char * reportxml_node_get_content(igloo_reportxml_node_t *node); +/* Adds, counts, and gets XML childs (used for ) */ +int reportxml_node_add_xml_child(igloo_reportxml_node_t *node, xmlNodePtr child); +ssize_t reportxml_node_count_xml_child(igloo_reportxml_node_t *node); +xmlNodePtr reportxml_node_get_xml_child(igloo_reportxml_node_t *node, size_t idx); + + +/* ---[ Database level ]--- */ +/* The database object is thread safe. */ + + +/* Add an report to the database */ +int reportxml_database_add_report(igloo_reportxml_database_t *db, igloo_reportxml_t *report); +/* Build a node (copy) from the data in the database based on the given ID (using "definition" and "defines" attributes) + * depth may be used to select how many recursions may be used to resolve definitions within defines. + * The default value is selected by passing -1 (recommended). + */ +igloo_reportxml_node_t * reportxml_database_build_node(igloo_reportxml_database_t *db, const char *id, ssize_t depth); +/* This does the same as reportxml_database_build_node() except that a new report document is returned. */ +igloo_reportxml_t * reportxml_database_build_report(igloo_reportxml_database_t *db, const char *id, ssize_t depth); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/igloo/types.h b/include/igloo/types.h index d9c4494..51a3de8 100644 --- a/include/igloo/types.h +++ b/include/igloo/types.h @@ -35,6 +35,10 @@ extern "C" { typedef struct igloo_buffer_tag igloo_buffer_t; typedef struct igloo_list_tag igloo_list_t; +typedef struct igloo_reportxml_tag igloo_reportxml_t; +typedef struct igloo_reportxml_node_tag igloo_reportxml_node_t; +typedef struct igloo_reportxml_database_tag igloo_reportxml_database_t; + /* * This header includes forward declarations for several basic types. */ @@ -49,6 +53,9 @@ typedef union __attribute__ ((__transparent_union__)) { igloo_RO_TYPE(igloo_ro_base_t) igloo_RO_TYPE(igloo_buffer_t) igloo_RO_TYPE(igloo_list_t) + igloo_RO_TYPE(igloo_reportxml_t) + igloo_RO_TYPE(igloo_reportxml_node_t) + igloo_RO_TYPE(igloo_reportxml_database_t) /* Now we add the current compilation unit's private types if any */ #ifdef igloo_RO_PRIVATETYPES diff --git a/src/reportxml.c b/src/reportxml.c new file mode 100644 index 0000000..8205b77 --- /dev/null +++ b/src/reportxml.c @@ -0,0 +1,1370 @@ +/* Icecast + * + * This program is distributed under the GNU General Public License, version 2. + * A copy of this license is included with this source. + * + * Copyright 2018, Philipp "ph3-der-loewe" Schafft , + */ + +/** + * Special fast event functions + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#define XMLSTR(str) ((xmlChar *)(str)) + +/* The report XML document type */ +struct igloo_reportxml_tag { + /* base object */ + igloo_ro_base_t __base; + /* the root report XML node of the document */ + igloo_reportxml_node_t *root; +}; + +/* The report XML node type */ +struct igloo_reportxml_node_tag { + /* base object */ + igloo_ro_base_t __base; + /* an XML node used to store the attributes */ + xmlNodePtr xmlnode; + /* the type of the node */ + reportxml_node_type_t type; + /* the report XML childs */ + igloo_reportxml_node_t **childs; + size_t childs_len; + /* the XML childs (used by ) */ + xmlNodePtr *xml_childs; + size_t xml_childs_len; + /* the node's text content (used by ) */ + char *content; +}; + +/* The report XML database type */ +struct igloo_reportxml_database_tag { + /* base object */ + igloo_ro_base_t __base; + /* the lock used to ensure the database object is thread safe. */ + igloo_mutex_t lock; + /* The tree of definitions */ + igloo_avl_tree *definitions; +}; + +/* The nodeattr structure is used to store definition of node attributes */ +struct nodeattr; +struct nodeattr { + /* name of the attribute */ + const char *name; + /* the type of the attribute. This is based on the DTD */ + const char *type; + /* the default value for the attribute if any */ + const char *def; + /* whether the attribute is required or not */ + int required; + /* a function that can be used to check the content of the attribute if any */ + int (*checker)(const struct nodeattr *attr, const char *str); + /* NULL terminated list of possible values (if enum) */ + const char *values[32]; +}; + +/* The type of the content an node has */ +enum nodecontent { + /* The node may not have any content */ + NC_NONE, + /* The node may have children */ + NC_CHILDS, + /* The node may have a text content */ + NC_CONTENT, + /* The node may have XML children */ + NC_XML +}; + +/* This structure is used to define a node */ +struct nodedef { + /* the type of the node */ + reportxml_node_type_t type; + /* the name of the corresponding XML node */ + const char *name; + /* The type of the content the node may have */ + enum nodecontent content; + /* __attr__eol terminated list of attributes the node may have */ + const struct nodeattr *attr[12]; + /* REPORTXML_NODE_TYPE__ERROR terminated list of child node types the node may have */ + const reportxml_node_type_t childs[12]; +}; + +static void __report_free(igloo_ro_t self); +static int __report_new(igloo_ro_t self, const igloo_ro_type_t *type, va_list ap); +static void __report_node_free(igloo_ro_t self); +static void __database_free(igloo_ro_t self); +static int __database_new(igloo_ro_t self, const igloo_ro_type_t *type, va_list ap); + +igloo_RO_PUBLIC_TYPE(igloo_reportxml_t, + igloo_RO_TYPEDECL_FREE(__report_free), + igloo_RO_TYPEDECL_NEW(__report_new) + ); + +igloo_RO_PUBLIC_TYPE(igloo_reportxml_node_t, + igloo_RO_TYPEDECL_FREE(__report_node_free) + ); + +igloo_RO_PUBLIC_TYPE(igloo_reportxml_database_t, + igloo_RO_TYPEDECL_FREE(__database_free), + igloo_RO_TYPEDECL_NEW(__database_new) + ); + +/* Prototypes */ +static int __attach_copy_of_node_or_definition(igloo_reportxml_node_t *parent, igloo_reportxml_node_t *node, igloo_reportxml_database_t *db, ssize_t depth); +static igloo_reportxml_node_t * __reportxml_database_build_node_ext(igloo_reportxml_database_t *db, const char *id, ssize_t depth, reportxml_node_type_t *acst_type_ret); + +/* definition of known attributes */ +static const struct nodeattr __attr__eol[1] = {{NULL, NULL, NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_version[1] = {{"version", "CDATA", "0.0.1", 1, NULL, {"0.0.1", NULL}}}; +static const struct nodeattr __attr_xmlns[1] = {{"xmlns", "URI", "http://icecast.org/specs/reportxml-0.0.1", 1, NULL, {"http://icecast.org/specs/reportxml-0.0.1", NULL}}}; +static const struct nodeattr __attr_id[1] = {{"id", "ID", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_definition[1] = {{"definition", "UUID", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr__definition[1] = {{"_definition", "UUID", NULL, 0, NULL, {NULL}}}; /* for internal use only */ +static const struct nodeattr __attr_akindof[1] = {{"akindof", "UUIDs", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_lang[1] = {{"lang", "LanguageCode", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_dir[1] = {{"dir", NULL, NULL, 0, NULL, {"ltr", "rtl", NULL}}}; +static const struct nodeattr __attr_template[1] = {{"template", "UUID", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_defines[1] = {{"defines", "UUID", NULL, 1, NULL, {NULL}}}; +static const struct nodeattr __attr_function[1] = {{"function", "CDATA", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_filename[1] = {{"filename", "CDATA", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_line[1] = {{"line", "CDATA", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_binary[1] = {{"binary", "CDATA", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_offset[1] = {{"offset", "CDATA", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_absolute[1] = {{"absolute", "iso8601", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_relative[1] = {{"relative", "iso8601", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_name[1] = {{"name", "CDATA", NULL, 1, NULL, {NULL}}}; +static const struct nodeattr __attr_member[1] = {{"member", "CDATA", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_value[1] = {{"value", "CDATA", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_state[1] = {{"state", NULL, "set", 1, NULL, {"declared", "set", "uninitialized", "missing", NULL}}}; +static const struct nodeattr __attr_href[1] = {{"href", "URI", NULL, 0, NULL, {NULL}}}; +static const struct nodeattr __attr_application[1] = {{"application", "URI", NULL, 1, NULL, {NULL}}}; +static const struct nodeattr __attr__action_type[1] = {{"type", NULL, NULL, 1, NULL, {"retry", "choice", "see-other", "authenticate", "pay", "change-protocol", "slow-down", "ask-user", "ask-admin", "bug", NULL}}}; +static const struct nodeattr __attr__resource_type[1] = {{"type", NULL, NULL, 1, NULL, {"actor", "manipulation-target", "helper", "related", "result", "parameter", NULL}}}; +static const struct nodeattr __attr__value_type[1] = {{"type", NULL, NULL, 1, NULL, {"null", "int", "float", "uuid", "string", "structure", "uri", "pointer", "version", "protocol", "username", "password", NULL}}}; +static const struct nodeattr __attr__reference_type[1] = {{"type", NULL, NULL, 1, NULL, {"documentation", "log", "report", "related", NULL}}}; + +/* definition of known nodes */ +/* Helper: + * grep '^ *REPORTXML_NODE_TYPE_' reportxml.h | sed 's/^\( *REPORTXML_NODE_TYPE_\([^,]*\)\),*$/\1 \2/;' | while read l s; do c=$(tr A-Z a-z <<<"$s"); printf " {%-32s \"%-16s 0, {__attr__eol}},\n" "$l," "$c\","; done + */ +#define __BASIC_ELEMENT __attr_id, __attr_definition, __attr_akindof, __attr__definition +static const struct nodedef __nodedef[] = { + {REPORTXML_NODE_TYPE_REPORT, "report", NC_CHILDS, {__attr_id, __attr_version, __attr_xmlns, __attr__eol}, + {REPORTXML_NODE_TYPE_INCIDENT, REPORTXML_NODE_TYPE_DEFINITION, REPORTXML_NODE_TYPE_TIMESTAMP, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_DEFINITION, "definition", NC_CHILDS, {__BASIC_ELEMENT, __attr_template, __attr_defines, __attr__eol}, + {REPORTXML_NODE_TYPE_INCIDENT, REPORTXML_NODE_TYPE_STATE, REPORTXML_NODE_TYPE_TIMESTAMP, REPORTXML_NODE_TYPE_RESOURCE, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_FIX, REPORTXML_NODE_TYPE_RESOURCE, REPORTXML_NODE_TYPE_REASON, REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_INCIDENT, "incident", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}, + {REPORTXML_NODE_TYPE_STATE, REPORTXML_NODE_TYPE_TIMESTAMP, REPORTXML_NODE_TYPE_RESOURCE, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_FIX, REPORTXML_NODE_TYPE_REASON, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_STATE, "state", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}, + {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_TIMESTAMP, REPORTXML_NODE_TYPE_BACKTRACE, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_BACKTRACE, "backtrace", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}, + {REPORTXML_NODE_TYPE_POSITION, REPORTXML_NODE_TYPE_MORE, REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_POSITION, "position", NC_CHILDS, {__BASIC_ELEMENT, __attr_function, __attr_filename, __attr_line, __attr_binary, __attr_offset, __attr__eol}, + {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_MORE, "more", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}, + {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_FIX, "fix", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}, + {REPORTXML_NODE_TYPE_ACTION, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_ACTION, "action", NC_CHILDS, {__BASIC_ELEMENT, __attr__action_type, __attr__eol}, + {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_TIMESTAMP, REPORTXML_NODE_TYPE_VALUE, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_REASON, "reason", NC_CHILDS, {__BASIC_ELEMENT, __attr__eol}, + {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_RESOURCE, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_TEXT, "text", NC_CONTENT, {__BASIC_ELEMENT, __attr_lang, __attr_dir, __attr__eol}, + {REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_TIMESTAMP, "timestamp", NC_NONE, {__BASIC_ELEMENT, __attr_absolute, __attr_relative, __attr__eol}, + {REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_RESOURCE, "resource", NC_CHILDS, {__BASIC_ELEMENT, __attr__resource_type, __attr_name, __attr__eol}, + {REPORTXML_NODE_TYPE_VALUE, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_VALUE, "value", NC_CHILDS, {__BASIC_ELEMENT, __attr_member, __attr_value, __attr_state, __attr__value_type, __attr__eol}, + {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_REFERENCE, REPORTXML_NODE_TYPE_VALUE, REPORTXML_NODE_TYPE_POSITION, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_REFERENCE, "reference", NC_CHILDS, {__BASIC_ELEMENT, __attr__reference_type, __attr_href, __attr__eol}, + {REPORTXML_NODE_TYPE_TEXT, REPORTXML_NODE_TYPE_EXTENSION, REPORTXML_NODE_TYPE__ERROR}}, + {REPORTXML_NODE_TYPE_EXTENSION, "extension", NC_XML, {__BASIC_ELEMENT, __attr_application, __attr__eol}, + {REPORTXML_NODE_TYPE__ERROR}} +}; + +static const struct nodedef * __get_nodedef(reportxml_node_type_t type) +{ + size_t i; + + for (i = 0; i < (sizeof(__nodedef)/sizeof(*__nodedef)); i++) { + if (__nodedef[i].type == type) { + return &(__nodedef[i]); + } + } + + return NULL; +} + +static const struct nodedef * __get_nodedef_by_name(const char *name) +{ + size_t i; + + for (i = 0; i < (sizeof(__nodedef)/sizeof(*__nodedef)); i++) { + if (strcmp(__nodedef[i].name, name) == 0) { + return &(__nodedef[i]); + } + } + + return NULL; +} + +static const struct nodeattr * __get_nodeattr(const struct nodedef * nodedef, const char *key) +{ + size_t i; + + if (!nodedef || !key) + return NULL; + + for (i = 0; i < (sizeof(nodedef->attr)/sizeof(*nodedef->attr)); i++) { + if (nodedef->attr[i]->name == NULL) { + /* we reached the end of the list */ + return NULL; + } + + if (strcmp(nodedef->attr[i]->name, key) == 0) { + return nodedef->attr[i]; + } + } + + return NULL; +} + + +static void __report_free(igloo_ro_t self) +{ + igloo_reportxml_t *report = igloo_RO_TO_TYPE(self, igloo_reportxml_t); + + igloo_ro_unref(report->root); +} + +static int __report_new(igloo_ro_t self, const igloo_ro_type_t *type, va_list ap) +{ + igloo_reportxml_t *ret = igloo_RO_TO_TYPE(self, igloo_reportxml_t); + igloo_reportxml_node_t *root = reportxml_node_new(REPORTXML_NODE_TYPE_REPORT, NULL, NULL, NULL); + + if (!root) + return -1; + + ret->root = root; + + return 0; +} + +static igloo_reportxml_t * reportxml_new_with_root(igloo_reportxml_node_t *root) +{ + igloo_reportxml_t *ret; + + if (!root) + return NULL; + + ret = igloo_ro_new_raw(igloo_reportxml_t, NULL, igloo_RO_NULL); + if (!ret) + return NULL; + + ret->root = root; + + return ret; +} + +igloo_reportxml_node_t * reportxml_get_root_node(igloo_reportxml_t *report) +{ + if (!report) + return NULL; + + if (igloo_ro_ref(report->root) != 0) + return NULL; + + return report->root; +} + +igloo_reportxml_node_t * reportxml_get_node_by_attribute(igloo_reportxml_t *report, const char *key, const char *value, int include_definitions) +{ + if (!report || !key || !value) + return NULL; + + return reportxml_node_get_child_by_attribute(report->root, key, value, include_definitions); +} + +igloo_reportxml_node_t * reportxml_get_node_by_type(igloo_reportxml_t *report, reportxml_node_type_t type, int include_definitions) +{ + if (!report) + return NULL; + + return reportxml_node_get_child_by_type(report->root, type, include_definitions); +} + +igloo_reportxml_t * reportxml_parse_xmldoc(xmlDocPtr doc) +{ + igloo_reportxml_node_t *root; + igloo_reportxml_t *ret; + xmlNodePtr xmlroot; + + if (!doc) + return NULL; + + xmlroot = xmlDocGetRootElement(doc); + if (!xmlroot) + return NULL; + + root = reportxml_node_parse_xmlnode(xmlroot); + if (!root) + return NULL; + + if (reportxml_node_get_type(root) != REPORTXML_NODE_TYPE_REPORT) { + igloo_ro_unref(root); + return NULL; + } + + ret = reportxml_new_with_root(root); + if (!ret) { + igloo_ro_unref(root); + return NULL; + } + + return ret; +} + +xmlDocPtr reportxml_render_xmldoc(igloo_reportxml_t *report) +{ + xmlDocPtr ret; + xmlNodePtr node; + + if (!report) + return NULL; + + node = reportxml_node_render_xmlnode(report->root); + if (!node) + return NULL; + + ret = xmlNewDoc(XMLSTR("1.0")); + if (!ret) { + xmlFreeNode(node); + return NULL; + } + + xmlDocSetRootElement(ret, node); + + return ret; +} + +static void __report_node_free(igloo_ro_t self) +{ + size_t i; + + igloo_reportxml_node_t *node = igloo_RO_TO_TYPE(self, igloo_reportxml_node_t); + xmlFreeNode(node->xmlnode); + + for (i = 0; i < node->childs_len; i++) { + igloo_ro_unref(node->childs[i]); + } + + for (i = 0; i < node->xml_childs_len; i++) { + xmlFreeNode(node->xml_childs[i]); + } + + free(node->content); + free(node->childs); + free(node->xml_childs); +} + +igloo_reportxml_node_t * reportxml_node_new(reportxml_node_type_t type, const char *id, const char *definition, const char *akindof) +{ + igloo_reportxml_node_t *ret; + const struct nodedef *nodedef = __get_nodedef(type); + size_t i; + + if (!nodedef) + return NULL; + + ret = igloo_ro_new_raw(igloo_reportxml_node_t, NULL, igloo_RO_NULL); + + if (ret == NULL) + return NULL; + + ret->type = type; + + ret->xmlnode = xmlNewNode(NULL, XMLSTR(nodedef->name)); + + if (!ret->xmlnode) { + igloo_ro_unref(ret); + return NULL; + } + + for (i = 0; i < (sizeof(nodedef->attr)/sizeof(*nodedef->attr)); i++) { + const struct nodeattr *nodeattr = nodedef->attr[i]; + if (nodeattr->name == NULL) + break; + + if (nodeattr->def) { + if (reportxml_node_set_attribute(ret, nodeattr->name, nodeattr->def) != 0) { + igloo_ro_unref(ret); + return NULL; + } + } + } + +#define _set_attr(x) \ + if ((x)) { \ + if (reportxml_node_set_attribute(ret, # x , (x)) != 0) { \ + igloo_ro_unref(ret); \ + return NULL; \ + } \ + } + + _set_attr(id) + _set_attr(definition) + _set_attr(akindof) + + return ret; +} + +igloo_reportxml_node_t * reportxml_node_parse_xmlnode(xmlNodePtr xmlnode) +{ + igloo_reportxml_node_t *node; + + const struct nodedef *nodedef; + + if (!xmlnode) + return NULL; + + nodedef = __get_nodedef_by_name((const char *)xmlnode->name); + if (!nodedef) { + return NULL; + } + + node = reportxml_node_new(nodedef->type, NULL, NULL, NULL); + if (!node) + return NULL; + + if (xmlnode->properties) { + xmlAttrPtr cur = xmlnode->properties; + + do { + xmlChar *value = xmlNodeListGetString(xmlnode->doc, cur->children, 1); + if (!value) { + igloo_ro_unref(node); + return NULL; + } + + if (reportxml_node_set_attribute(node, (const char*)cur->name, (const char*)value) != 0) { + igloo_ro_unref(node); + return NULL; + } + + xmlFree(value); + } while ((cur = cur->next)); + } + + if (xmlnode->xmlChildrenNode) { + xmlNodePtr cur = xmlnode->xmlChildrenNode; + + do { + if (nodedef->content == NC_XML) { + if (reportxml_node_add_xml_child(node, cur) != 0) { + igloo_ro_unref(node); + return NULL; + } + } else { + igloo_reportxml_node_t *child; + + if (xmlIsBlankNode(cur)) + continue; + + if (cur->type == XML_COMMENT_NODE) + continue; + + if (cur->type == XML_TEXT_NODE) { + xmlChar *value = xmlNodeListGetString(xmlnode->doc, cur, 1); + + if (!value) { + igloo_ro_unref(node); + return NULL; + } + + if (reportxml_node_set_content(node, (const char *)value) != 0) { + igloo_ro_unref(node); + return NULL; + } + + xmlFree(value); + continue; + } + + child = reportxml_node_parse_xmlnode(cur); + if (!child) { + igloo_ro_unref(node); + return NULL; + } + + if (reportxml_node_add_child(node, child) != 0) { + igloo_ro_unref(child); + igloo_ro_unref(node); + return NULL; + } + igloo_ro_unref(child); + } + } while ((cur = cur->next)); + } + + return node; +} + +static igloo_reportxml_node_t * __reportxml_node_copy_with_db(igloo_reportxml_node_t *node, igloo_reportxml_database_t *db, ssize_t depth) +{ + igloo_reportxml_node_t *ret; + ssize_t child_count; + ssize_t xml_child_count; + size_t i; + + if (!node) + return NULL; + + child_count = reportxml_node_count_child(node); + if (child_count < 0) + return NULL; + + xml_child_count = reportxml_node_count_xml_child(node); + if (xml_child_count < 0) + return NULL; + + ret = reportxml_node_parse_xmlnode(node->xmlnode); + if (!ret) + return NULL; + + if (node->content) { + if (reportxml_node_set_content(ret, node->content) != 0) { + igloo_ro_unref(ret); + return NULL; + } + } + + for (i = 0; i < (size_t)child_count; i++) { + igloo_reportxml_node_t *child = reportxml_node_get_child(node, i); + + if (db && depth > 0) { + if (__attach_copy_of_node_or_definition(ret, child, db, depth) != 0) { + igloo_ro_unref(child); + igloo_ro_unref(ret); + return NULL; + } + igloo_ro_unref(child); + } else { + igloo_reportxml_node_t *copy = __reportxml_node_copy_with_db(child, NULL, -1); + + igloo_ro_unref(child); + + if (!copy) { + igloo_ro_unref(ret); + return NULL; + } + + if (reportxml_node_add_child(ret, copy) != 0) { + igloo_ro_unref(copy); + igloo_ro_unref(ret); + return NULL; + } + + igloo_ro_unref(copy); + } + } + + for (i = 0; i < (size_t)xml_child_count; i++) { + xmlNodePtr child = reportxml_node_get_xml_child(node, i); + if (reportxml_node_add_xml_child(ret, child) != 0) { + xmlFreeNode(child); + igloo_ro_unref(ret); + return NULL; + } + xmlFreeNode(child); + } + + return ret; +} + +igloo_reportxml_node_t * reportxml_node_copy(igloo_reportxml_node_t *node) +{ + return __reportxml_node_copy_with_db(node, NULL, -1); +} + +xmlNodePtr reportxml_node_render_xmlnode(igloo_reportxml_node_t *node) +{ + xmlNodePtr ret; + ssize_t child_count; + ssize_t xml_child_count; + size_t i; + xmlChar *definition; + + if (!node) + return NULL; + + child_count = reportxml_node_count_child(node); + if (child_count < 0) + return NULL; + + xml_child_count = reportxml_node_count_xml_child(node); + if (xml_child_count < 0) + return NULL; + + ret = xmlCopyNode(node->xmlnode, 2); + if (!ret) + return NULL; + + definition = xmlGetProp(ret, XMLSTR("_definition")); + if (definition) { + xmlSetProp(ret, XMLSTR("definition"), definition); + xmlUnsetProp(ret, XMLSTR("_definition")); + xmlFree(definition); + } + + for (i = 0; i < (size_t)child_count; i++) { + igloo_reportxml_node_t *child = reportxml_node_get_child(node, i); + xmlNodePtr xmlchild; + + if (!child) { + xmlFreeNode(ret); + return NULL; + } + + xmlchild = reportxml_node_render_xmlnode(child); + igloo_ro_unref(child); + if (!xmlchild) { + xmlFreeNode(ret); + return NULL; + } + + xmlAddChild(ret, xmlchild); + } + + if (node->content) { + xmlNodePtr xmlchild = xmlNewText(XMLSTR(node->content)); + + if (!xmlchild) { + xmlFreeNode(ret); + return NULL; + } + + xmlAddChild(ret, xmlchild); + } + + for (i = 0; i < (size_t)xml_child_count; i++) { + xmlNodePtr xmlchild = reportxml_node_get_xml_child(node, i); + + if (!xmlchild) { + xmlFreeNode(ret); + return NULL; + } + + xmlAddChild(ret, xmlchild); + } + + + return ret; +} + +reportxml_node_type_t reportxml_node_get_type(igloo_reportxml_node_t *node) +{ + if (!node) + return REPORTXML_NODE_TYPE__ERROR; + + return node->type; +} + +int reportxml_node_set_attribute(igloo_reportxml_node_t *node, const char *key, const char *value) +{ + const struct nodedef *nodedef; + const struct nodeattr *nodeattr; + size_t i; + + if (!node || !key || !value) + return -1; + + nodedef = __get_nodedef(node->type); + nodeattr = __get_nodeattr(nodedef, key); + if (!nodeattr) + return -1; + + if (nodeattr->values[0] != NULL) { + int found = 0; + for (i = 0; i < (sizeof(nodeattr->values)/sizeof(*nodeattr->values)); i++) { + if (nodeattr->values[i] == NULL) + break; + + if (strcmp(nodeattr->values[i], value) == 0) { + found = 1; + break; + } + } + + if (!found) + return -1; + } + + if (nodeattr->checker) { + if (nodeattr->checker(nodeattr, value) != 0) { + return -1; + } + } + + xmlSetProp(node->xmlnode, XMLSTR(key), XMLSTR(value)); + + return 0; +} + +char * reportxml_node_get_attribute(igloo_reportxml_node_t *node, const char *key) +{ + xmlChar *k; + char *n; + + if (!node || !key) + return NULL; + + /* We do this super complicated thing as we do not know if we can use free() to free a xmlChar* object. */ + k = xmlGetProp(node->xmlnode, XMLSTR(key)); + if (!k) + return NULL; + + n = strdup((char*)k); + xmlFree(k); + + return n; +} + +int reportxml_node_add_child(igloo_reportxml_node_t *node, igloo_reportxml_node_t *child) +{ + const struct nodedef *nodedef; + igloo_reportxml_node_t **n; + size_t i; + int found; + + if (!node || !child) + return -1; + + nodedef = __get_nodedef(node->type); + + if (nodedef->content != NC_CHILDS) + return -1; + + found = 0; + for (i = 0; nodedef->childs[i] != REPORTXML_NODE_TYPE__ERROR; i++) { + if (nodedef->childs[i] == child->type) { + found = 1; + break; + } + } + + if (!found) + return -1; + + n = realloc(node->childs, sizeof(*n)*(node->childs_len + 1)); + if (!n) + return -1; + + node->childs = n; + + if (igloo_ro_ref(child) != 0) + return -1; + + node->childs[node->childs_len++] = child; + + return 0; +} + +ssize_t reportxml_node_count_child(igloo_reportxml_node_t *node) +{ + if (!node) + return -1; + + return node->childs_len; +} + +igloo_reportxml_node_t * reportxml_node_get_child(igloo_reportxml_node_t *node, size_t idx) +{ + if (!node) + return NULL; + + if (idx >= node->childs_len) + return NULL; + + if (igloo_ro_ref(node->childs[idx]) != 0) + return NULL; + + return node->childs[idx]; +} + +igloo_reportxml_node_t * reportxml_node_get_child_by_attribute(igloo_reportxml_node_t *node, const char *key, const char *value, int include_definitions) +{ + igloo_reportxml_node_t *ret; + xmlChar *k; + size_t i; + + if (!node || !key ||!value) + return NULL; + + k = xmlGetProp(node->xmlnode, XMLSTR(key)); + if (k != NULL) { + if (strcmp((const char*)k, value) == 0) { + xmlFree(k); + + if (igloo_ro_ref(node) != 0) + return NULL; + + return node; + } + xmlFree(k); + } + + if (node->type == REPORTXML_NODE_TYPE_DEFINITION && !include_definitions) + return NULL; + + for (i = 0; i < node->childs_len; i++) { + ret = reportxml_node_get_child_by_attribute(node->childs[i], key, value, include_definitions); + if (ret != NULL) + return ret; + } + + return NULL; +} + +igloo_reportxml_node_t * reportxml_node_get_child_by_type(igloo_reportxml_node_t *node, reportxml_node_type_t type, int include_definitions) +{ + size_t i; + + if (!node) + return NULL; + + if (node->type == type) { + if (igloo_ro_ref(node) != 0) + return NULL; + return node; + } + + if (node->type == REPORTXML_NODE_TYPE_DEFINITION && !include_definitions) + return NULL; + + for (i = 0; i < node->childs_len; i++) { + igloo_reportxml_node_t *ret; + + ret = reportxml_node_get_child_by_type(node->childs[i], type, include_definitions); + if (ret != NULL) + return ret; + } + + return NULL; +} + +int reportxml_node_set_content(igloo_reportxml_node_t *node, const char *value) +{ + const struct nodedef *nodedef; + char *n = NULL; + + if (!node) + return -1; + + nodedef = __get_nodedef(node->type); + + if (nodedef->content != NC_CONTENT) + return -1; + + if (value) { + n = strdup(value); + if (!n) + return -1; + } + + free(node->content); + node->content = n; + + return 0; +} + +char * reportxml_node_get_content(igloo_reportxml_node_t *node) +{ + if (!node) + return NULL; + + if (node->content) { + return strdup(node->content); + } else { + return NULL; + } +} + +int reportxml_node_add_xml_child(igloo_reportxml_node_t *node, xmlNodePtr child) +{ + const struct nodedef *nodedef; + xmlNodePtr *n; + + if (!node || !child) + return -1; + + nodedef = __get_nodedef(node->type); + + if (nodedef->content != NC_XML) + return -1; + + n = realloc(node->xml_childs, sizeof(*n)*(node->xml_childs_len + 1)); + if (!n) + return -1; + + node->xml_childs = n; + + node->xml_childs[node->xml_childs_len] = xmlCopyNode(child, 1); + if (node->xml_childs[node->xml_childs_len] == NULL) + return -1; + + node->xml_childs_len++; + + return 0; +} + +ssize_t reportxml_node_count_xml_child(igloo_reportxml_node_t *node) +{ + if (!node) + return -1; + + return node->xml_childs_len; +} + +xmlNodePtr reportxml_node_get_xml_child(igloo_reportxml_node_t *node, size_t idx) +{ + xmlNodePtr ret; + + if (!node) + return NULL; + + if (idx >= node->xml_childs_len) + return NULL; + + ret = xmlCopyNode(node->xml_childs[idx], 1); + + return ret; +} + +static void __database_free(igloo_ro_t self) +{ + igloo_reportxml_database_t *db = igloo_RO_TO_TYPE(self, igloo_reportxml_database_t); + + if (db->definitions) + igloo_avl_tree_free(db->definitions, (igloo_avl_free_key_fun_type)igloo_ro_unref); + + igloo_thread_mutex_destroy(&(db->lock)); +} + +static int __compare_definitions(void *arg, void *a, void *b) +{ + char *id_a, *id_b; + int ret = 0; + + id_a = reportxml_node_get_attribute(a, "defines"); + id_b = reportxml_node_get_attribute(b, "defines"); + + if (!id_a || !id_b || id_a == id_b) { + ret = 0; + } else { + ret = strcmp(id_a, id_b); + } + + free(id_a); + free(id_b); + + return ret; +} + +static int __database_new(igloo_ro_t self, const igloo_ro_type_t *type, va_list ap) +{ + igloo_reportxml_database_t *ret = igloo_RO_TO_TYPE(self, igloo_reportxml_database_t); + + ret->definitions = igloo_avl_tree_new(__compare_definitions, NULL); + if (!ret->definitions) { + return -1; + } + + igloo_thread_mutex_create(&(ret->lock)); + + return 0; +} + +int reportxml_database_add_report(igloo_reportxml_database_t *db, igloo_reportxml_t *report) +{ + igloo_reportxml_node_t *root; + ssize_t count; + size_t i; + + if (!db || !report) + return -1; + + root = reportxml_get_root_node(report); + if (!root) + return -1; + + count = reportxml_node_count_child(root); + if (count < 0) + return -1; + + igloo_thread_mutex_lock(&(db->lock)); + + for (i = 0; i < (size_t)count; i++) { + igloo_reportxml_node_t *node = reportxml_node_get_child(root, i); + igloo_reportxml_node_t *copy; + + if (reportxml_node_get_type(node) != REPORTXML_NODE_TYPE_DEFINITION) { + igloo_ro_unref(node); + continue; + } + + copy = reportxml_node_copy(node); + igloo_ro_unref(node); + + if (!copy) + continue; + + igloo_avl_insert(db->definitions, copy); + } + + igloo_thread_mutex_unlock(&(db->lock)); + + igloo_ro_unref(root); + + return 0; +} + +static int __attach_copy_of_node_or_definition(igloo_reportxml_node_t *parent, igloo_reportxml_node_t *node, igloo_reportxml_database_t *db, ssize_t depth) +{ + igloo_reportxml_node_t *copy; + igloo_reportxml_node_t *def = NULL; + char *definition; + + if (!parent || !node || !db) + return -1; + + + if (depth >= 2) { + definition = reportxml_node_get_attribute(node, "definition"); + if (definition) { + def = reportxml_database_build_node(db, definition, depth - 1); + free(definition); + } + } + + if (def) { + ssize_t count = reportxml_node_count_child(def); + size_t i; + + if (count < 0) { + igloo_ro_unref(def); + return -1; + } + + for (i = 0; i < (size_t)count; i++) { + igloo_reportxml_node_t *child = reportxml_node_get_child(def, i); + + if (__attach_copy_of_node_or_definition(parent, child, db, depth - 1) != 0) { + igloo_ro_unref(child); + igloo_ro_unref(def); + return -1; + } + + igloo_ro_unref(child); + } + + igloo_ro_unref(def); + + return 0; + } else { + int ret; + + copy = __reportxml_node_copy_with_db(node, db, depth - 1); + if (!copy) { + return -1; + } + + ret = reportxml_node_add_child(parent, copy); + + igloo_ro_unref(copy); + + return ret; + } +} + +static igloo_reportxml_node_t * __reportxml_database_build_node_ext(igloo_reportxml_database_t *db, const char *id, ssize_t depth, reportxml_node_type_t *acst_type_ret) +{ + igloo_reportxml_node_t *search; + igloo_reportxml_node_t *found; + igloo_reportxml_node_t *ret; + enum { + ACST_FIRST, + ACST_YES, + ACST_NO, + } all_childs_same_type = ACST_FIRST; + reportxml_node_type_t acst_type = REPORTXML_NODE_TYPE__ERROR; + char *template; + ssize_t count; + size_t i; + + if (!db || !id) + return NULL; + + /* Assign default depth in case it's set to -1 */ + if (depth < 0) + depth = 8; + + if (!depth) + return NULL; + + search = reportxml_node_new(REPORTXML_NODE_TYPE_DEFINITION, NULL, NULL, NULL); + if (!search) + return NULL; + + if (reportxml_node_set_attribute(search, "defines", id) != 0) { + igloo_ro_unref(search); + return NULL; + } + + igloo_thread_mutex_lock(&(db->lock)); + if (igloo_avl_get_by_key(db->definitions, igloo_RO_TO_TYPE(search, void *), (void**)&found) != 0) { + igloo_thread_mutex_unlock(&(db->lock)); + igloo_ro_unref(search); + return NULL; + } + + igloo_ro_unref(search); + + if (igloo_ro_ref(found) != 0) { + igloo_thread_mutex_unlock(&(db->lock)); + return NULL; + } + igloo_thread_mutex_unlock(&(db->lock)); + + count = reportxml_node_count_child(found); + if (count < 0) { + igloo_ro_unref(found); + return NULL; + } + + template = reportxml_node_get_attribute(found, "template"); + if (template) { + igloo_reportxml_node_t *tpl = reportxml_database_build_node(db, template, depth - 1); + + free(template); + + if (tpl) { + ret = reportxml_node_copy(tpl); + igloo_ro_unref(tpl); + } else { + ret = NULL; + } + } else { + ret = reportxml_node_new(REPORTXML_NODE_TYPE_DEFINITION, NULL, NULL, NULL); + } + + if (!ret) { + igloo_ro_unref(found); + return NULL; + } + + for (i = 0; i < (size_t)count; i++) { + /* TODO: Look up definitions of our childs and childs' childs. */ + + igloo_reportxml_node_t *node = reportxml_node_get_child(found, i); + reportxml_node_type_t type = reportxml_node_get_type(node); + + switch (all_childs_same_type) { + case ACST_FIRST: + acst_type = type; + all_childs_same_type = ACST_YES; + break; + case ACST_YES: + if (acst_type != type) + all_childs_same_type = ACST_NO; + break; + case ACST_NO: + /* noop */ + break; + } + + /* We want depth, not depth - 1 here. __attach_copy_of_node_or_definition() takes care of this for us. */ + if (__attach_copy_of_node_or_definition(ret, node, db, depth) != 0) { + igloo_ro_unref(node); + igloo_ro_unref(found); + igloo_ro_unref(ret); + return NULL; + } + + igloo_ro_unref(node); + } + + igloo_ro_unref(found); + + if (all_childs_same_type == ACST_YES) { + count = reportxml_node_count_child(ret); + if (count < 0) { + igloo_ro_unref(ret); + return NULL; + } + + for (i = 0; i < (size_t)count; i++) { + igloo_reportxml_node_t *node = reportxml_node_get_child(ret, i); + + if (!node) { + igloo_ro_unref(ret); + return NULL; + } + + if (reportxml_node_set_attribute(node, "_definition", id) != 0) { + igloo_ro_unref(node); + igloo_ro_unref(ret); + return NULL; + } + + igloo_ro_unref(node); + } + } + + if (acst_type_ret) { + if (all_childs_same_type == ACST_YES) { + *acst_type_ret = acst_type; + } else { + *acst_type_ret = REPORTXML_NODE_TYPE__ERROR; + } + } + + return ret; +} + +igloo_reportxml_node_t * reportxml_database_build_node(igloo_reportxml_database_t *db, const char *id, ssize_t depth) +{ + return __reportxml_database_build_node_ext(db, id, depth, NULL); +} + +/* We try to build a a report from the definition. Exat structure depends on what is defined. */ +igloo_reportxml_t * reportxml_database_build_report(igloo_reportxml_database_t *db, const char *id, ssize_t depth) +{ + igloo_reportxml_node_t *definition; + igloo_reportxml_node_t *child; + igloo_reportxml_node_t *root; + igloo_reportxml_node_t *attach_to; + reportxml_node_type_t type; + igloo_reportxml_t *ret; + ssize_t count; + size_t i; + + if (!db || !id) + return NULL; + + /* first find the definition itself. This will be some REPORTXML_NODE_TYPE_DEFINITION node. */ + definition = __reportxml_database_build_node_ext(db, id, depth, &type); + if (!definition) { + return NULL; + } + + /* Let's see how many children we have. */ + count = reportxml_node_count_child(definition); + if (count < 0) { + igloo_ro_unref(definition); + return NULL; + } else if (count == 0) { + /* Empty definition? Not exactly an exciting report... */ + igloo_ro_unref(definition); + return igloo_ro_new(igloo_reportxml_t); + } + + if (type == REPORTXML_NODE_TYPE__ERROR) { + /* Now the hard part: find out what level we are. */ + child = reportxml_node_get_child(definition, 0); + if (!child) { + igloo_ro_unref(definition); + return NULL; + } + + type = reportxml_node_get_type(child); + igloo_ro_unref(child); + } + + /* check for supported configurations */ + switch (type) { + case REPORTXML_NODE_TYPE_INCIDENT: + case REPORTXML_NODE_TYPE_STATE: + break; + default: + igloo_ro_unref(definition); + return NULL; + break; + } + + ret = igloo_ro_new(igloo_reportxml_t); + if (!ret) { + igloo_ro_unref(definition); + return NULL; + } + + root = reportxml_get_root_node(ret); + if (!ret) { + igloo_ro_unref(definition); + igloo_ro_unref(ret); + return NULL; + } + + if (type == REPORTXML_NODE_TYPE_INCIDENT) { + igloo_ro_ref(attach_to = root); + } else if (type == REPORTXML_NODE_TYPE_STATE) { + attach_to = reportxml_node_new(REPORTXML_NODE_TYPE_INCIDENT, NULL, NULL, NULL); + if (attach_to) { + if (reportxml_node_add_child(root, attach_to) != 0) { + igloo_ro_unref(attach_to); + attach_to = NULL; + } + } + } else { + attach_to = NULL; + } + + igloo_ro_unref(root); + + if (!attach_to) { + igloo_ro_unref(definition); + igloo_ro_unref(ret); + return NULL; + } + + /* now move nodes. */ + + for (i = 0; i < (size_t)count; i++) { + child = reportxml_node_get_child(definition, i); + + if (reportxml_node_get_type(child) == type) { + /* Attach definition to all childs that are the same type. + * As long as we work to-the-specs all childs are of the same type. + * But if we work in relaxed mode, there might be other tags. + */ + reportxml_node_set_attribute(child, "definition", id); + } + + /* we can directly attach as it's a already a copy. */ + if (reportxml_node_add_child(attach_to, child) != 0) { + igloo_ro_unref(definition); + igloo_ro_unref(attach_to); + igloo_ro_unref(ret); + return NULL; + } + + igloo_ro_unref(child); + } + + igloo_ro_unref(definition); + igloo_ro_unref(attach_to); + + return ret; +}