From 8c37c8567d24d94636cff13bab4eebd9ae690ca2 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Tue, 10 Jul 2007 23:25:18 +0000 Subject: Add NamedNodeMap. Minor fix for NodeList unref function; ensure it unrefs the owner document after it has finished using it. svn path=/trunk/dom/; revision=3395 --- src/core/Makefile | 2 +- src/core/document.c | 110 ++++++++++++++- src/core/document.h | 12 +- src/core/namednodemap.c | 345 ++++++++++++++++++++++++++++++++++++++++++++++++ src/core/namednodemap.h | 39 ++++++ src/core/nodelist.c | 13 +- 6 files changed, 514 insertions(+), 7 deletions(-) create mode 100644 src/core/namednodemap.c create mode 100644 src/core/namednodemap.h (limited to 'src/core') diff --git a/src/core/Makefile b/src/core/Makefile index 3245df3..cede355 100644 --- a/src/core/Makefile +++ b/src/core/Makefile @@ -22,7 +22,7 @@ CFLAGS += -I$(CURDIR) # Objects -OBJS = attr document node nodelist string +OBJS = attr document namednodemap node nodelist string .PHONY: clean debug distclean export release setup test diff --git a/src/core/document.c b/src/core/document.c index 71b3a6a..102153c 100644 --- a/src/core/document.c +++ b/src/core/document.c @@ -23,6 +23,16 @@ struct dom_doc_nl { struct dom_doc_nl *prev; /**< Previous item */ }; +/** + * Iten in list of active namednodemaps + */ +struct dom_doc_nnm { + struct dom_namednodemap *map; /**< Named node map */ + + struct dom_doc_nnm *next; /**< Next map */ + struct dom_doc_nnm *prev; /**< Previous map */ +}; + /** * DOM document */ @@ -31,6 +41,8 @@ struct dom_document { struct dom_doc_nl *nodelists; /**< List of active nodelists */ + struct dom_doc_nnm *maps; /**< List of active namednodemaps */ + dom_alloc alloc; /**< Memory (de)allocation function */ void *pw; /**< Pointer to client data */ }; @@ -809,7 +821,7 @@ dom_exception dom_document_get_nodelist(struct dom_document *doc, * 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.*/ + * dom_document_remove_nodelist. */ *list = l->list; @@ -849,3 +861,99 @@ void dom_document_remove_nodelist(struct dom_document *doc, /* And free item */ doc->alloc(l, 0, doc->pw); } + +/** + * Get a namednodemap, creating one if necessary + * + * \param doc The document to get a namednodemap for + * \param root Node containing items in map + * \param type The type of map + * \param map Pointer to location to receive map + * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion. + * + * The returned map will have its reference count increased. It is + * the responsibility of the caller to unref the map once it has + * finished with it. + */ +dom_exception dom_document_get_namednodemap(struct dom_document *doc, + struct dom_node *root, dom_namednodemap_type type, + struct dom_namednodemap **map) +{ + struct dom_doc_nnm *m; + dom_exception err; + + for (m = doc->maps; m; m = m->next) { + if (dom_namednodemap_match(m->map, root, type)) + break; + } + + if (m != NULL) { + /* Found an existing map, so use it */ + dom_namednodemap_ref(m->map); + } else { + /* No existing map */ + + /* Create active map entry */ + m = doc->alloc(NULL, sizeof(struct dom_doc_nnm), doc->pw); + if (m == NULL) + return DOM_NO_MEM_ERR; + + /* Create namednodemap */ + err = dom_namednodemap_create(doc, root, type, &m->map); + if (err != DOM_NO_ERR) { + doc->alloc(m, 0, doc->pw); + return err; + } + + /* Add to document's list of active namednodemaps */ + m->prev = NULL; + m->next = doc->maps; + if (doc->maps) + doc->maps->prev = m; + doc->maps = m; + } + + /* Note: the document does not claim a reference on the namednodemap + * If it did, the map's reference count would never reach zero, + * and the list would remain indefinitely. This is not a problem as + * the map notifies the document of its destruction via + * dom_document_remove_namednodempa. */ + + *map = m->map; + + return DOM_NO_ERR; +} + +/** + * Remove a namednodemap + * + * \param doc The document to remove the map from + * \param map The map to remove + */ +void dom_document_remove_namednodemap(struct dom_document *doc, + struct dom_namednodemap *map) +{ + struct dom_doc_nnm *m; + + for (m = doc->maps; m; m = m->next) { + if (m->map == map) + break; + } + + if (m == NULL) { + /* This should never happen; we should probably abort here */ + return; + } + + /* Remove from list */ + if (m->prev != NULL) + m->prev->next = m->next; + else + doc->maps = m->next; + + if (m->next != NULL) + m->next->prev = m->prev; + + /* And free item */ + doc->alloc(m, 0, doc->pw); +} diff --git a/src/core/document.h b/src/core/document.h index a51d1e1..236ab4a 100644 --- a/src/core/document.h +++ b/src/core/document.h @@ -11,7 +11,10 @@ #include #include +#include "core/namednodemap.h" + struct dom_document; +struct dom_namednodemap; struct dom_node; struct dom_nodelist; struct dom_string; @@ -27,9 +30,16 @@ 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); +/* Get a namednodemap, creating one if necessary */ +dom_exception dom_document_get_namednodemap(struct dom_document *doc, + struct dom_node *root, dom_namednodemap_type type, + struct dom_namednodemap **map); +/* Remove a namednodemap */ +void dom_document_remove_namednodemap(struct dom_document *doc, + struct dom_namednodemap *map); + #endif diff --git a/src/core/namednodemap.c b/src/core/namednodemap.c new file mode 100644 index 0000000..b13dee6 --- /dev/null +++ b/src/core/namednodemap.c @@ -0,0 +1,345 @@ +/* + * 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 "core/document.h" +#include "core/namednodemap.h" + +#include "utils/utils.h" + +/** + * DOM named node map + */ +struct dom_namednodemap { + struct dom_document *owner; /**< Owning document */ + + struct dom_node *root; /**< Node containing items in map */ + + dom_namednodemap_type type; /**< Type of map */ + + uint32_t refcnt; /**< Reference count */ +}; + +/** + * Create a namednodemap + * + * \param doc The owning document + * \param root Node containing items in map + * \param type The type of map + * \param map Pointer to location to receive created map + * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion + * + * ::root must be a node owned by ::doc and must be either an Element or + * DocumentType node. + * + * If ::root is of type Element, ::type must be DOM_NAMEDNODEMAP_ATTRIBUTES + * If ::root is of type DocumentType, ::type may be either + * DOM_NAMEDNODEMAP_ENTITIES or DOM_NAMEDNODEMAP_NOTATIONS. + * + * The returned map will already be referenced, so the client need not + * explicitly reference it. The client must unref the map once it is + * finished with it. + */ +dom_exception dom_namednodemap_create(struct dom_document *doc, + struct dom_node *root, dom_namednodemap_type type, + struct dom_namednodemap **map) +{ + struct dom_namednodemap *m; + + m = dom_document_alloc(doc, NULL, sizeof(struct dom_namednodemap)); + if (m == NULL) + return DOM_NO_MEM_ERR; + + dom_node_ref((struct dom_node *) doc); + m->owner = doc; + + dom_node_ref(root); + m->root = root; + + m->type = type; + + m->refcnt = 1; + + *map = m; + + return DOM_NO_ERR; +} + +/** + * Claim a reference on a DOM named node map + * + * \param map The map to claim a reference on + */ +void dom_namednodemap_ref(struct dom_namednodemap *map) +{ + map->refcnt++; +} + +/** + * Release a reference on a DOM named node map + * + * \param map The map to release the reference from + * + * If the reference count reaches zero, any memory claimed by the + * map will be released + */ +void dom_namednodemap_unref(struct dom_namednodemap *map) +{ + if (--map->refcnt == 0) { + struct dom_node *owner = (struct dom_node *) map->owner; + + dom_node_unref(map->root); + + /* Remove map from document */ + dom_document_remove_namednodemap(map->owner, map); + + /* Destroy the map object */ + dom_document_alloc(map->owner, map, 0); + + /* And release our reference on the owning document + * This must be last as, otherwise, it's possible that + * the document is destroyed before we are */ + dom_node_unref(owner); + } +} + +/** + * Retrieve the length of a named node map + * + * \param map Map to retrieve length of + * \param length Pointer to location to receive length + * \return DOM_NO_ERR. + */ +dom_exception dom_namednodemap_get_length(struct dom_namednodemap *map, + unsigned long *length) +{ + UNUSED(map); + UNUSED(length); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Retrieve an item by name from a named node map + * + * \param map The map to retrieve the item from + * \param name The name of the item to retrieve + * \param node Pointer to location to receive item + * \return DOM_NO_ERR. + * + * 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_namednodemap_get_named_item(struct dom_namednodemap *map, + struct dom_string *name, struct dom_node **node) +{ + UNUSED(map); + UNUSED(name); + UNUSED(node); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Add a node to a named node map, replacing any matching existing node + * + * \param map The map to add to + * \param arg The node to add + * \param node Pointer to location to receive replaced node + * \return DOM_NO_ERR on success, + * DOM_WRONG_DOCUMENT_ERR if ::arg was created from a + * different document than ::map, + * DOM_NO_MODIFICATION_ALLOWED_ERR if ::map is readonly, + * DOM_INUSE_ATTRIBUTE_ERR if ::arg is an Attr that is + * already an attribute on another + * Element, + * DOM_HIERARCHY_REQUEST_ERR if the type of ::arg is not + * permitted as a member of ::map. + * + * ::arg's nodeName attribute will be used to store it in ::map. It will + * be accessible using the nodeName attribute as the key for lookup. + * + * Replacing a node by itself has no effect. + * + * 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_namednodemap_set_named_item(struct dom_namednodemap *map, + struct dom_node *arg, struct dom_node **node) +{ + UNUSED(map); + UNUSED(arg); + UNUSED(node); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Remove an item by name from a named node map + * + * \param map The map to remove from + * \param name The name of the item to remove + * \param node Pointer to location to receive removed item + * \return DOM_NO_ERR on success, + * DOM_NOT_FOUND_ERR if there is no node named ::name + * in ::map, + * DOM_NO_MODIFICATION_ALLOWED_ERR if ::map is readonly. + * + * 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_namednodemap_remove_named_item( + struct dom_namednodemap *map, struct dom_string *name, + struct dom_node **node) +{ + UNUSED(map); + UNUSED(name); + UNUSED(node); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Retrieve an item from a named node map + * + * \param map The map to retrieve the item from + * \param index The map index to retrieve + * \param node Pointer to location to receive item + * \return DOM_NO_ERR. + * + * ::index is a zero-based index into ::map. + * ::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_namednodemap_item(struct dom_namednodemap *map, + unsigned long index, struct dom_node **node) +{ + UNUSED(map); + UNUSED(index); + UNUSED(node); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Retrieve an item by namespace/localname from a named node map + * + * \param map The map to retrieve the item from + * \param namespace The namespace URI of the item to retrieve + * \param localname The local name of the node to retrieve + * \param node Pointer to location to receive item + * \return DOM_NO_ERR on success, + * DOM_NOT_SUPPORTED_ERR if the implementation does not support the + * feature "XML" and the language exposed + * through the Document does not support + * Namespaces. + * + * 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_namednodemap_get_named_item_ns( + struct dom_namednodemap *map, struct dom_string *namespace, + struct dom_string *localname, struct dom_node **node) +{ + UNUSED(map); + UNUSED(namespace); + UNUSED(localname); + UNUSED(node); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Add a node to a named node map, replacing any matching existing node + * + * \param map The map to add to + * \param arg The node to add + * \param node Pointer to location to receive replaced node + * \return DOM_NO_ERR on success, + * DOM_WRONG_DOCUMENT_ERR if ::arg was created from a + * different document than ::map, + * DOM_NO_MODIFICATION_ALLOWED_ERR if ::map is readonly, + * DOM_INUSE_ATTRIBUTE_ERR if ::arg is an Attr that is + * already an attribute on another + * Element, + * DOM_HIERARCHY_REQUEST_ERR if the type of ::arg is not + * permitted as a member of ::map. + * DOM_NOT_SUPPORTED_ERR if the implementation does not support the + * feature "XML" and the language exposed + * through the Document does not support + * Namespaces. + * + * ::arg's namespaceURI and localName attributes will be used to store it in + * ::map. It will be accessible using the namespaceURI and localName + * attributes as the keys for lookup. + * + * Replacing a node by itself has no effect. + * + * 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_namednodemap_set_named_item_ns( + struct dom_namednodemap *map, struct dom_node *arg, + struct dom_node **node) +{ + UNUSED(map); + UNUSED(arg); + UNUSED(node); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Remove an item by namespace/localname from a named node map + * + * \param map The map to remove from + * \param namespace The namespace URI of the item to remove + * \param localname The local name of the item to remove + * \param node Pointer to location to receive removed item + * \return DOM_NO_ERR on success, + * DOM_NOT_FOUND_ERR if there is no node named ::name + * in ::map, + * DOM_NO_MODIFICATION_ALLOWED_ERR if ::map is readonly. + * DOM_NOT_SUPPORTED_ERR if the implementation does not support the + * feature "XML" and the language exposed + * through the Document does not support + * Namespaces. + * + * 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_namednodemap_remove_named_item_ns( + struct dom_namednodemap *map, struct dom_string *namespace, + struct dom_string *localname, struct dom_node **node) +{ + UNUSED(map); + UNUSED(namespace); + UNUSED(localname); + UNUSED(node); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Match a namednodemap instance against a set of creation parameters + * + * \param map The map to match + * \param root Node containing items in map + * \param type The type of map + * \return true if list matches, false otherwise + */ +bool dom_namednodemap_match(struct dom_namednodemap *map, + struct dom_node *root, dom_namednodemap_type type) +{ + if (map->root == root && map->type == type) + return true; + + return false; +} diff --git a/src/core/namednodemap.h b/src/core/namednodemap.h new file mode 100644 index 0000000..1cd2b0c --- /dev/null +++ b/src/core/namednodemap.h @@ -0,0 +1,39 @@ +/* + * 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_namednodemap_h_ +#define dom_internal_namednodemap_h_ + +#include + +#include + +struct dom_document; +struct dom_node; +struct dom_namednodemap; +struct dom_string; + +/** + * Type of a named node map + */ +typedef enum { + DOM_NAMEDNODEMAP_ATTRIBUTES, + DOM_NAMEDNODEMAP_ENTITIES, + DOM_NAMEDNODEMAP_NOTATIONS +} dom_namednodemap_type; + +/* Create a namednodemap */ +dom_exception dom_namednodemap_create(struct dom_document *doc, + struct dom_node *root, dom_namednodemap_type type, + struct dom_namednodemap **map); + + +/* Match a namednodemap instance against a set of creation parameters */ +bool dom_namednodemap_match(struct dom_namednodemap *map, + struct dom_node *root, dom_namednodemap_type type); + +#endif diff --git a/src/core/nodelist.c b/src/core/nodelist.c index 1fff561..b37b84e 100644 --- a/src/core/nodelist.c +++ b/src/core/nodelist.c @@ -62,7 +62,7 @@ struct dom_nodelist { * 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. + * do so explicitly. The client must unref the list once finished with it. */ dom_exception dom_nodelist_create(struct dom_document *doc, struct dom_node *root, struct dom_string *tagname, @@ -124,6 +124,8 @@ void dom_nodelist_ref(struct dom_nodelist *list) void dom_nodelist_unref(struct dom_nodelist *list) { if (--list->refcnt == 0) { + struct dom_node *owner = (struct dom_node *) list->owner; + switch (list->type) { case DOM_NODELIST_CHILDREN: /* Nothing to do */ @@ -139,13 +141,16 @@ void dom_nodelist_unref(struct dom_nodelist *list) 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 */ + /* Destroy the list object */ dom_document_alloc(list->owner, list, 0); + + /* And release our reference on the owning document + * This must be last as, otherwise, it's possible that + * the document is destroyed before we are */ + dom_node_unref(owner); } } -- cgit v1.2.3