From acfc9fd1b44f7b72ba66705daf34914940509793 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Tue, 10 Jul 2007 21:36:14 +0000 Subject: Add NodeList and string comparison API svn path=/trunk/dom/; revision=3394 --- src/core/Makefile | 2 +- src/core/attr.c | 2 + src/core/document.c | 114 ++++++++++++++++++++++++++ src/core/document.h | 16 ++++ src/core/node.c | 2 + src/core/node.h | 2 +- src/core/nodelist.c | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/core/nodelist.h | 31 +++++++ src/core/string.c | 54 +++++++++++++ 9 files changed, 449 insertions(+), 2 deletions(-) create mode 100644 src/core/nodelist.c create mode 100644 src/core/nodelist.h (limited to 'src') diff --git a/src/core/Makefile b/src/core/Makefile index 3924ebb..3245df3 100644 --- a/src/core/Makefile +++ b/src/core/Makefile @@ -22,7 +22,7 @@ CFLAGS += -I$(CURDIR) # Objects -OBJS = attr document node string +OBJS = attr document node nodelist string .PHONY: clean debug distclean export release setup test diff --git a/src/core/attr.c b/src/core/attr.c index 5414ce0..1a5926b 100644 --- a/src/core/attr.c +++ b/src/core/attr.c @@ -5,6 +5,8 @@ * Copyright 2007 John-Mark Bell */ +#include + #include #include "core/node.h" diff --git a/src/core/document.c b/src/core/document.c index 6edce6e..71b3a6a 100644 --- a/src/core/document.c +++ b/src/core/document.c @@ -10,14 +10,27 @@ #include "core/document.h" #include "core/node.h" +#include "core/nodelist.h" #include "utils/utils.h" +/** + * Item in list of active nodelists + */ +struct dom_doc_nl { + struct dom_nodelist *list; /**< Nodelist */ + + struct dom_doc_nl *next; /**< Next item */ + struct dom_doc_nl *prev; /**< Previous item */ +}; + /** * DOM document */ struct dom_document { struct dom_node base; /**< Base node */ + struct dom_doc_nl *nodelists; /**< List of active nodelists */ + dom_alloc alloc; /**< Memory (de)allocation function */ void *pw; /**< Pointer to client data */ }; @@ -735,3 +748,104 @@ void *dom_document_alloc(struct dom_document *doc, void *ptr, size_t size) { return doc->alloc(ptr, size, doc->pw); } + +/** + * Get a nodelist, creating one if necessary + * + * \param doc The document to get a nodelist for + * \param root Root node of subtree that list applies to + * \param tagname Name of nodes in list (or NULL) + * \param namespace Namespace part of nodes in list (or NULL) + * \param localname Local part of nodes in list (or NULL) + * \param list Pointer to location to receive list + * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion + * + * The returned list will have its reference count increased. It is + * the responsibility of the caller to unref the list once it has + * finished with it. + */ +dom_exception dom_document_get_nodelist(struct dom_document *doc, + struct dom_node *root, struct dom_string *tagname, + struct dom_string *namespace, struct dom_string *localname, + struct dom_nodelist **list) +{ + struct dom_doc_nl *l; + dom_exception err; + + for (l = doc->nodelists; l; l = l->next) { + if (dom_nodelist_match(l->list, root, tagname, + namespace, localname)) + break; + } + + if (l != NULL) { + /* Found an existing list, so use it */ + dom_nodelist_ref(l->list); + } else { + /* No existing list */ + + /* Create active list entry */ + l = doc->alloc(NULL, sizeof(struct dom_doc_nl), doc->pw); + if (l == NULL) + return DOM_NO_MEM_ERR; + + /* Create nodelist */ + err = dom_nodelist_create(doc, root, tagname, namespace, + localname, &l->list); + if (err != DOM_NO_ERR) { + doc->alloc(l, 0, doc->pw); + return err; + } + + /* Add to document's list of active nodelists */ + l->prev = NULL; + l->next = doc->nodelists; + if (doc->nodelists) + doc->nodelists->prev = l; + doc->nodelists = l; + } + + /* Note: the document does not claim a reference on the nodelist + * If it did, the nodelist's reference count would never reach zero, + * and the list would remain indefinitely. This is not a problem as + * the list notifies the document of its destruction via + * dom_document_remove_nodelist.*/ + + *list = l->list; + + return DOM_NO_ERR; +} + +/** + * Remove a nodelist from a document + * + * \param doc The document to remove the list from + * \param list The list to remove + */ +void dom_document_remove_nodelist(struct dom_document *doc, + struct dom_nodelist *list) +{ + struct dom_doc_nl *l; + + for (l = doc->nodelists; l; l = l->next) { + if (l->list == list) + break; + } + + if (l == NULL) { + /* This should never happen; we should probably abort here */ + return; + } + + /* Remove from list */ + if (l->prev != NULL) + l->prev->next = l->next; + else + doc->nodelists = l->next; + + if (l->next != NULL) + l->next->prev = l->prev; + + /* And free item */ + doc->alloc(l, 0, doc->pw); +} diff --git a/src/core/document.h b/src/core/document.h index 7c18fe9..a51d1e1 100644 --- a/src/core/document.h +++ b/src/core/document.h @@ -12,8 +12,24 @@ #include struct dom_document; +struct dom_node; +struct dom_nodelist; +struct dom_string; +/* Get base of document buffer */ const uint8_t *dom_document_get_base(struct dom_document *doc); + +/* (De)allocate memory */ void *dom_document_alloc(struct dom_document *doc, void *ptr, size_t size); +/* Get a nodelist, creating one if necessary */ +dom_exception dom_document_get_nodelist(struct dom_document *doc, + struct dom_node *root, struct dom_string *tagname, + struct dom_string *namespace, struct dom_string *localname, + struct dom_nodelist **list); + +/* Remove a nodelist */ +void dom_document_remove_nodelist(struct dom_document *doc, + struct dom_nodelist *list); + #endif diff --git a/src/core/node.c b/src/core/node.c index 468965b..40848df 100644 --- a/src/core/node.c +++ b/src/core/node.c @@ -5,6 +5,8 @@ * Copyright 2007 John-Mark Bell */ +#include + #include "core/document.h" #include "core/node.h" #include "utils/utils.h" diff --git a/src/core/node.h b/src/core/node.h index bfd7fa0..d8982f6 100644 --- a/src/core/node.h +++ b/src/core/node.h @@ -9,9 +9,9 @@ #define dom_internal_core_node_h_ #include -#include struct dom_attr; +struct dom_string; /** * User data context attached to a DOM node diff --git a/src/core/nodelist.c b/src/core/nodelist.c new file mode 100644 index 0000000..1fff561 --- /dev/null +++ b/src/core/nodelist.c @@ -0,0 +1,228 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2007 John-Mark Bell + */ + +#include +#include +#include + +#include "core/document.h" +#include "core/nodelist.h" + +#include "utils/utils.h" + +struct dom_node; + +/** + * DOM node list + */ +struct dom_nodelist { + struct dom_document *owner; /**< Owning document */ + + struct dom_node *root; /**< Root of applicable subtree */ + + enum { DOM_NODELIST_CHILDREN, + DOM_NODELIST_BY_NAME, + DOM_NODELIST_BY_NAMESPACE + } type; /**< List type */ + + union { + struct dom_string *name; /**< Tag name to match */ + struct { + struct dom_string *namespace; /**< Namespace */ + struct dom_string *localname; /**< Localname */ + } ns; /**< Data for namespace matching */ + } data; + + uint32_t refcnt; /**< Reference count */ +}; + +/** + * Create a nodelist + * + * \param doc Owning document + * \param root Root node of subtree that list applies to + * \param tagname Name of nodes in list (or NULL) + * \param namespace Namespace part of nodes in list (or NULL) + * \param localname Local part of nodes in list (or NULL) + * \param list Pointer to location to receive list + * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion + * + * ::root must be a node owned by ::doc + * + * If ::tagname is non-NULL, ::namespace and ::localname must be NULL and + * the created list will match nodes by name + * If ::namespace is non-NULL, ::localname must be non-NULL and + * ::tagname must be NULL and the created list will match nodes by namespace + * and localname + * If ::tagname, ::namespace and ::localname are NULL, the created list + * will match the children of ::root. + * + * The returned list will already be referenced, so the client need not + * do so explicitly. The client should unref the list once finished with it. + */ +dom_exception dom_nodelist_create(struct dom_document *doc, + struct dom_node *root, struct dom_string *tagname, + struct dom_string *namespace, struct dom_string *localname, + struct dom_nodelist **list) +{ + struct dom_nodelist *l; + + l = dom_document_alloc(doc, NULL, sizeof(struct dom_nodelist)); + if (l == NULL) + return DOM_NO_MEM_ERR; + + dom_node_ref((struct dom_node *) doc); + l->owner = doc; + + dom_node_ref(root); + l->root = root; + + if (tagname != NULL && namespace == NULL && localname == NULL) { + dom_string_ref(tagname); + l->type = DOM_NODELIST_BY_NAME; + l->data.name = tagname; + } else if (namespace != NULL && localname != NULL && + tagname == NULL) { + dom_string_ref(namespace); + dom_string_ref(localname); + l->type = DOM_NODELIST_BY_NAMESPACE; + l->data.ns.namespace = namespace; + l->data.ns.localname = localname; + } else { + l->type = DOM_NODELIST_CHILDREN; + } + + l->refcnt = 1; + + *list = l; + + return DOM_NO_ERR; +} + +/** + * Claim a reference on a DOM node list + * + * \param list The list to claim a reference on + */ +void dom_nodelist_ref(struct dom_nodelist *list) +{ + list->refcnt++; +} + +/** + * Release a reference on a DOM node list + * + * \param list The list to release the reference from + * + * If the reference count reaches zero, any memory claimed by the + * list will be released + */ +void dom_nodelist_unref(struct dom_nodelist *list) +{ + if (--list->refcnt == 0) { + switch (list->type) { + case DOM_NODELIST_CHILDREN: + /* Nothing to do */ + break; + case DOM_NODELIST_BY_NAMESPACE: + dom_string_unref(list->data.ns.namespace); + dom_string_unref(list->data.ns.localname); + break; + case DOM_NODELIST_BY_NAME: + dom_string_unref(list->data.name); + break; + } + + dom_node_unref(list->root); + + dom_node_unref((struct dom_node *) list->owner); + + /* Remove list from document */ + dom_document_remove_nodelist(list->owner, list); + + /* And destroy the list object */ + dom_document_alloc(list->owner, list, 0); + } +} + +/** + * Retrieve the length of a node list + * + * \param list List to retrieve length of + * \param length Pointer to location to receive length + * \return DOM_NO_ERR. + */ +dom_exception dom_nodelist_get_length(struct dom_nodelist *list, + unsigned long *length) +{ + UNUSED(list); + UNUSED(length); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Retrieve an item from a node list + * + * \param list The list to retrieve the item from + * \param index The list index to retrieve + * \param node Pointer to location to receive item + * \return DOM_NO_ERR. + * + * ::index is a zero-based index into ::list. + * ::index lies in the range [0, length-1] + * + * The returned node will have had its reference count increased. The client + * should unref the node once it has finished with it. + */ +dom_exception dom_nodelist_item(struct dom_nodelist *list, + unsigned long index, struct dom_node **node) +{ + UNUSED(list); + UNUSED(index); + UNUSED(node); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Match a nodelist instance against a set of nodelist creation parameters + * + * \param list List to match + * \param root Root node of subtree that list applies to + * \param tagname Name of nodes in list (or NULL) + * \param namespace Namespace part of nodes in list (or NULL) + * \param localname Local part of nodes in list (or NULL) + * \return true if list matches, false otherwise + */ +bool dom_nodelist_match(struct dom_nodelist *list, struct dom_node *root, + struct dom_string *tagname, struct dom_string *namespace, + struct dom_string *localname) +{ + if (list->root != root) + return false; + + if (list->type == DOM_NODELIST_CHILDREN && tagname == NULL && + namespace == NULL && localname == NULL) { + return true; + } + + if (list->type == DOM_NODELIST_BY_NAME && tagname != NULL && + namespace == NULL && localname == NULL) { + return (dom_string_cmp(list->data.name, tagname) == 0); + } + + if (list->type == DOM_NODELIST_BY_NAMESPACE && tagname == NULL && + namespace != NULL && localname != NULL) { + return (dom_string_cmp(list->data.ns.namespace, + namespace) == 0) && + (dom_string_cmp(list->data.ns.localname, + localname) == 0); + } + + return false; +} diff --git a/src/core/nodelist.h b/src/core/nodelist.h new file mode 100644 index 0000000..1e96f8d --- /dev/null +++ b/src/core/nodelist.h @@ -0,0 +1,31 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2007 John-Mark Bell + */ + +#ifndef dom_internal_nodelist_h_ +#define dom_internal_nodelist_h_ + +#include + +#include + +struct dom_document; +struct dom_node; +struct dom_nodelist; +struct dom_string; + +/* Create a nodelist */ +dom_exception dom_nodelist_create(struct dom_document *doc, + struct dom_node *root, struct dom_string *tagname, + struct dom_string *namespace, struct dom_string *localname, + struct dom_nodelist **list); + +/* Match a nodelist instance against a set of nodelist creation parameters */ +bool dom_nodelist_match(struct dom_nodelist *list, struct dom_node *root, + struct dom_string *tagname, struct dom_string *namespace, + struct dom_string *localname); + +#endif diff --git a/src/core/string.c b/src/core/string.c index a60fa94..d73ce8d 100644 --- a/src/core/string.c +++ b/src/core/string.c @@ -217,3 +217,57 @@ dom_exception dom_string_get_data(struct dom_string *str, return DOM_NO_ERR; } + +/** + * Case sensitively compare two DOM strings + * + * \param s1 The first string to compare + * \param s2 The second string to compare + * \return 0 if strings match, non-0 otherwise + */ +int dom_string_cmp(struct dom_string *s1, struct dom_string *s2) +{ + const uint8_t *d1, *d2; + size_t l1, l2; + dom_exception err; + + err = dom_string_get_data(s1, &d1, &l1); + if (err != DOM_NO_ERR) + return 1; /* arbitrary */ + + err = dom_string_get_data(s2, &d2, &l2); + if (err != DOM_NO_ERR) + return 1; /* arbitrary */ + + if (l1 != l2) + return 1; /* arbitrary */ + + return strncmp((const char *) d1, (const char *) d2, l1); +} + +/** + * Case insensitively compare two DOM strings + * + * \param s1 The first string to compare + * \param s2 The second string to compare + * \return 0 if strings match, non-0 otherwise + */ +int dom_string_icmp(struct dom_string *s1, struct dom_string *s2) +{ + const uint8_t *d1, *d2; + size_t l1, l2; + dom_exception err; + + err = dom_string_get_data(s1, &d1, &l1); + if (err != DOM_NO_ERR) + return 1; /* arbitrary */ + + err = dom_string_get_data(s2, &d2, &l2); + if (err != DOM_NO_ERR) + return 1; /* arbitrary */ + + if (l1 != l2) + return 1; /* arbitrary */ + + return strncasecmp((const char *) d1, (const char *) d2, l1); +} -- cgit v1.2.3