/* DOMC Document Object Model library in C * Copyright (c) 2001 Michael B. Allen * * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /* nodelist.c - the DOM_NodeList interface */ #include "defines.h" #include #include "domc.h" #include "dom.h" #if FAST_NODELIST /* The number of nodes required in a list before hashing starts */ #define FAST_FILLFACTOR 16 static void _removeFromMap(DOM_NodeList* nl, DOM_Node* key) { if (nl->_map) { if (hashmap_get(nl->_map, key) != NULL) { void* d = NULL; void* k = key; hashmap_remove(nl->_map, &k, &d); } } } static int _addToMap(DOM_NodeList* nl, DOM_Node* key, NodeEntry* val) { if (!nl->_map && nl->length > FAST_FILLFACTOR) { nl->_map = hashmap_new(0, NULL, NULL, NULL); /* Hash what we currently have */ if (nl->_map) { NodeEntry *e = nl->first; while (e) { _addToMap(nl, e->node, e); e = e->next; } } } if (nl->_map) { _removeFromMap(nl, key); if (hashmap_put(nl->_map, key, val) == -1) { DOM_Exception = errno; return -1; } } return 0; } #endif static NodeEntry* _lookupNode(DOM_NodeList* nl, DOM_Node* node) { NodeEntry* s; #if FAST_NODELIST if (nl->_map) s = (NodeEntry*)hashmap_get(nl->_map, node); else #endif for (s = nl->first; s != NULL && s->node != node; s = s->next) { ; } return s; } /* NodeList */ DOM_NodeList * Document_createNodeList(DOM_Document *doc) { DOM_NodeList *r; if ((r = calloc(sizeof *r, 1)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); } r->_ownerDocument = doc; return r; } void DOM_Document_destroyNodeList(DOM_Document *doc, DOM_NodeList *nl, int free_nodes) { if (nl) { if (nl->filter == 0) { NodeEntry *e, *tmp; e = nl->first; while (e != NULL) { if (free_nodes) { DOM_Document_destroyNode(doc, e->node); } tmp = e; e = e->next; free(tmp); } } #if FAST_NODELIST if(nl->_map) hashmap_del(nl->_map, NULL, NULL, NULL); #endif free(nl); } } DOM_Node * NodeList_itemFiltered(const DOM_NodeList *list, int index, unsigned short nodeType) { if (list && index >= 0 && index < list->length) { NodeEntry *e; for (e = list->first; e != NULL; e = e->next) { if (e->node->nodeType == nodeType) { if (index == 0) { return e->node; } index--; } } } return NULL; } DOM_Node * DOM_NodeList_item(const DOM_NodeList *list, int index) { if (list) { if (list->filter) { return NodeList_itemFiltered(list->list, index, list->filter); } if (index >= 0 && index < list->length) { NodeEntry *e; for (e = list->first; e != NULL; e = e->next, index--) { if (index == 0) { return e->node; } } } } return NULL; } NodeEntry * NodeList_insert(DOM_NodeList *nl, DOM_Node *newChild, DOM_Node *refChild) { NodeEntry *e; NodeEntry *s = NULL; if (nl == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (nl->filter) { DOM_Exception = DOM_FILTERED_LIST_ERR; PMNO(DOM_Exception); return NULL; } if(refChild != NULL) { s = _lookupNode(nl, refChild); if(s == NULL || s->node != refChild) { DOM_Exception = DOM_NOT_FOUND_ERR; PMNO(DOM_Exception); return NULL; } } if ((e = calloc(sizeof *e, 1)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); return NULL; } #if FAST_NODELIST if (_addToMap(nl, newChild, e) == -1) { PMNO(DOM_Exception); free(e); return NULL; } #endif e->node = newChild; if (nl->length == 0) { nl->first = nl->last = e; } else if (refChild == NULL) { e->prev = nl->last; nl->last->next = e; nl->last = e; } else { e->prev = s->prev; e->next = s; if (s == nl->first) { nl->first = e; } else { s->prev->next = e; } s->prev = e; } nl->length++; /* If an attribute is being added this is probably a NamedNodeMap * in which case we must set the ownerElement. */ if (newChild->nodeType == DOM_ATTRIBUTE_NODE) { newChild->u.Attr.ownerElement = nl->_ownerElement; } return e; } NodeEntry * NodeList_replace(DOM_NodeList *nl, DOM_Node *newChild, DOM_Node *oldChild) { NodeEntry *e; if (nl == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (nl->filter) { DOM_Exception = DOM_FILTERED_LIST_ERR; PMNO(DOM_Exception); return NULL; } e = _lookupNode(nl, oldChild); if (e == NULL) { DOM_Exception = DOM_NOT_FOUND_ERR; PMNO(DOM_Exception); return NULL; } #if FAST_NODELIST _removeFromMap(nl, oldChild); if(_addToMap(nl, newChild, e) == -1) { PMNO(DOM_Exception); return NULL; } #endif e->node = newChild; if (oldChild->nodeType == DOM_ATTRIBUTE_NODE) { oldChild->u.Attr.ownerElement = NULL; } return e; } NodeEntry * NodeList_remove(DOM_NodeList *nl, DOM_Node *oldChild) { NodeEntry *e; if (nl == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (nl->filter) { DOM_Exception = DOM_FILTERED_LIST_ERR; PMNO(DOM_Exception); return NULL; } e = _lookupNode(nl, oldChild); if (e == NULL) { return NULL; } #if FAST_NODELIST _removeFromMap(nl, oldChild); #endif if (nl->first == nl->last) { nl->first = nl->last = NULL; } else if (e == nl->first) { nl->first = e->next; nl->first->prev = NULL; } else if (e == nl->last) { nl->last = e->prev; nl->last->next = NULL; } else { e->prev->next = e->next; e->next->prev = e->prev; } nl->length--; /* Decrement a filtered node list too? */ if (oldChild->nodeType == DOM_ATTRIBUTE_NODE) { oldChild->u.Attr.ownerElement = NULL; } return e; } extern const char *node_names[]; NodeEntry * NodeList_append(DOM_NodeList *nl, DOM_Node *newChild) { NodeEntry *e; DOM_DocumentType *doctype; if (nl == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNF(DOM_Exception, ": %p", newChild); return NULL; } if (nl->filter) { DOM_Exception = DOM_FILTERED_LIST_ERR; PMNO(DOM_Exception); return NULL; } if ((e = calloc(sizeof *e, 1)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); return NULL; } #if FAST_NODELIST if(_addToMap(nl, newChild, e) == -1) { PMNO(DOM_Exception); free(e); return NULL; } #endif e->node = newChild; if (nl->first == NULL) { nl->first = nl->last = e; } else { nl->last->next = e; e->prev = nl->last; nl->last = e; } nl->length++; /* If the node list is the DocumentType children and a Notation * or Entity is being added we must artificially update the length * member of those filtered lists */ if (newChild->ownerDocument && (doctype = newChild->ownerDocument->u.Document.doctype) && nl == doctype->childNodes) { if (newChild->nodeType == DOM_NOTATION_NODE) { doctype->u.DocumentType.notations->length++; } else if (newChild->nodeType == DOM_ENTITY_NODE) { doctype->u.DocumentType.entities->length++; } } /* If an attribute is being added this is probably a NamedNodeMap * in which case we must set the ownerElement. */ if (newChild->nodeType == DOM_ATTRIBUTE_NODE) { newChild->u.Attr.ownerElement = nl->_ownerElement; } return e; } int NodeList_exists(DOM_NodeList *nl, DOM_Node *child) { NodeEntry *e; if (nl == NULL || nl->filter) { return 0; } e = _lookupNode(nl, child); return e != NULL; }