diff options
Diffstat (limited to 'src/core/element.c')
-rw-r--r-- | src/core/element.c | 2031 |
1 files changed, 1431 insertions, 600 deletions
diff --git a/src/core/element.c b/src/core/element.c index 80d547e..11c7f5e 100644 --- a/src/core/element.c +++ b/src/core/element.c @@ -3,31 +3,122 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org> + * Copyright 2009 Bo Yang <struggleyb.nku@gmail.com> */ #include <stdio.h> +#include <string.h> +#include <assert.h> +#include <dom/dom.h> #include <dom/core/attr.h> #include <dom/core/element.h> #include <dom/core/node.h> #include <dom/core/string.h> +#include <dom/core/document.h> #include "core/attr.h" #include "core/document.h" #include "core/element.h" #include "core/node.h" +#include "core/namednodemap.h" +#include "utils/validate.h" #include "utils/namespace.h" #include "utils/utils.h" +#include "utils/hashtable.h" + +/* The three numbers are just random ones, maybe we should change it after some + * more consideration */ +#define CHAINS_ATTRIBUTES 31 +#define CHAINS_NAMESPACE 7 +#define CHAINS_NS_ATTRIBUTES 31 static struct dom_element_vtable element_vtable = { { - DOM_NODE_VTABLE + DOM_NODE_VTABLE_ELEMENT }, DOM_ELEMENT_VTABLE }; +static struct dom_node_protect_vtable element_protect_vtable = { + DOM_ELEMENT_PROTECT_VTABLE +}; + +static dom_exception _dom_element_get_attr(struct dom_element *element, + struct dom_hash_table *hs, struct dom_string *name, + struct dom_string **value); +static dom_exception _dom_element_set_attr(struct dom_element *element, + struct dom_hash_table *hs, struct dom_string *name, + struct dom_string *value); +static dom_exception _dom_element_remove_attr(struct dom_element *element, + struct dom_hash_table *hs, struct dom_string *name); +static dom_exception _dom_element_get_attr_node(struct dom_element *element, + struct dom_hash_table *hs, struct dom_string *name, + struct dom_attr **result); +static dom_exception _dom_element_set_attr_node(struct dom_element *element, + struct dom_hash_table *hs, struct dom_attr *attr, + struct dom_attr **result); +static dom_exception _dom_element_remove_attr_node(struct dom_element *element, + struct dom_hash_table *hs, struct dom_attr *attr, + struct dom_attr **result); +static dom_exception _dom_element_has_attr(struct dom_element *element, + struct dom_hash_table *hs, struct dom_string *name, + bool *result); +static dom_exception _dom_element_set_id_attr(struct dom_element *element, + struct dom_hash_table *hs, struct dom_string *name, bool is_id); + +static unsigned int _dom_element_hash_lwcstring(void *key); + + +/* The operation set for namednodemap */ +static dom_exception attributes_get_length(void *priv, + unsigned long *length); +static dom_exception attributes_get_named_item(void *priv, + struct dom_string *name, struct dom_node **node); +static dom_exception attributes_set_named_item(void *priv, + struct dom_node *arg, struct dom_node **node); +static dom_exception attributes_remove_named_item( + void *priv, struct dom_string *name, + struct dom_node **node); +static dom_exception attributes_item(void *priv, + unsigned long index, struct dom_node **node); +static dom_exception attributes_get_named_item_ns( + void *priv, struct dom_string *namespace, + struct dom_string *localname, struct dom_node **node); +static dom_exception attributes_set_named_item_ns( + void *priv, struct dom_node *arg, + struct dom_node **node); +static dom_exception attributes_remove_named_item_ns( + void *priv, struct dom_string *namespace, + struct dom_string *localname, struct dom_node **node); +static void attributes_destroy(void *priv); +static bool attributes_equal(void *p1, void *p2); + +static struct nnm_operation attributes_opt = { + attributes_get_length, + attributes_get_named_item, + attributes_set_named_item, + attributes_remove_named_item, + attributes_item, + attributes_get_named_item_ns, + attributes_set_named_item_ns, + attributes_remove_named_item_ns, + attributes_destroy, + attributes_equal +}; + +static void *_key(void *key, void *key_pw, dom_alloc alloc, void *pw, + bool clone); +static void *_value(void *value, void *value_pw, dom_alloc alloc, + void *pw, bool clone); +static void *_nsattributes(void *value, void *value_pw, dom_alloc alloc, + void *pw, bool clone); + +/*----------------------------------------------------------------------*/ +/* Constructors and Destructors */ + /** - * Initialise an element node + * Create an element node * * \param doc The owning document * \param name The (local) name of the node to create @@ -43,41 +134,29 @@ static struct dom_element_vtable element_vtable = { * * The returned element will already be referenced. */ -dom_exception dom_element_initialise(struct dom_element *el, - struct dom_string *name, struct dom_string *namespace, - struct dom_string *prefix, struct dom_element **result) +dom_exception _dom_element_create(struct dom_document *doc, + struct lwc_string_s *name, struct lwc_string_s *namespace, + struct lwc_string_s *prefix, struct dom_element **result) { - dom_exception err; - struct dom_document *doc; - - dom_node_get_owner_document(el, &doc); - - /** \todo Sanity check the tag name */ - - /* Initialise the base class */ - err = dom_node_initialise(&el->base, doc, DOM_ELEMENT_NODE, - name, NULL, namespace, prefix); - if (err != DOM_NO_ERR) { - dom_document_alloc(doc, el, 0); - return err; - } + struct dom_element *el; - /* Perform our type-specific initialisation */ - el->attributes = NULL; - el->schema_type_info = NULL; + /* Allocate the element */ + el = _dom_document_alloc(doc, NULL, sizeof(struct dom_element)); + if (el == NULL) + return DOM_NO_MEM_ERR; - /* Init the vtable's destroy function */ + /* Initialise the vtables */ el->base.base.vtable = &element_vtable; - el->base.destroy = &_dom_element_destroy; + el->base.vtable = &element_protect_vtable; - *result = el; - - return DOM_NO_ERR; + return _dom_element_initialise(el, doc, name, namespace, prefix, + result); } /** - * Create an element node + * Initialise an element node * + * \param el The element * \param doc The owning document * \param name The (local) name of the node to create * \param namespace The namespace URI of the element, or NULL @@ -87,24 +166,51 @@ dom_exception dom_element_initialise(struct dom_element *el, * DOM_INVALID_CHARACTER_ERR if ::name is invalid, * DOM_NO_MEM_ERR on memory exhaustion. * + * The caller should make sure that ::name is a valid NCName. + * * ::doc, ::name, ::namespace and ::prefix will have their * reference counts increased. * * The returned element will already be referenced. */ -dom_exception dom_element_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *namespace, - struct dom_string *prefix, struct dom_element **result) +dom_exception _dom_element_initialise(struct dom_element *el, + struct dom_document *doc, struct lwc_string_s *name, + struct lwc_string_s *namespace, struct lwc_string_s *prefix, + struct dom_element **result) { - struct dom_element *el; + dom_exception err; - /* Allocate the element */ - el = dom_document_alloc(doc, NULL, sizeof(struct dom_element)); - if (el == NULL) - return DOM_NO_MEM_ERR; + assert(doc != NULL); + + err = _dom_document_create_hashtable(doc, CHAINS_ATTRIBUTES, + _dom_element_hash_lwcstring, &el->attributes); + if (err != DOM_NO_ERR) { + _dom_document_alloc(doc, el, 0); + return err; + } + + err = _dom_document_create_hashtable(doc, CHAINS_NAMESPACE, + _dom_element_hash_lwcstring, &el->ns_attributes); + if (err != DOM_NO_ERR) { + _dom_document_alloc(doc, el, 0); + _dom_document_alloc(doc, el->attributes, 0); + return err; + } + /* Initialise the base class */ + err = _dom_node_initialise(&el->base, doc, DOM_ELEMENT_NODE, + name, NULL, namespace, prefix); + if (err != DOM_NO_ERR) { + _dom_document_alloc(doc, el, 0); + return err; + } + + /* Perform our type-specific initialisation */ + el->id_ns = NULL; + el->id_name = NULL; + el->schema_type_info = NULL; + + *result = el; - dom_element_initialise(el, name, namespace, prefix, result); - return DOM_NO_ERR; } @@ -116,54 +222,22 @@ dom_exception dom_element_create(struct dom_document *doc, * * The contents of ::element will be destroyed and ::element will be freed. */ -void dom_element_destroy(struct dom_document *doc, +void _dom_element_destroy(struct dom_document *doc, struct dom_element *element) { - struct dom_node_internal *c, *d; - - /* Destroy children of this node */ - for (c = element->base.first_child; c != NULL; c = d) { - d = c->next; - - /* Detach child */ - c->parent = NULL; - - if (c->refcnt > 0) { - /* Something is using this child */ - - /** \todo add to list of nodes pending deletion */ - - continue; - } - - /* Detach from sibling list */ - c->previous = NULL; - c->next = NULL; - - dom_node_destroy(c); - } + lwc_context *ctx = _dom_document_get_intern_context(doc); + assert (ctx != NULL); /* Destroy attributes attached to this node */ - for (c = (struct dom_node_internal *) element->attributes; - c != NULL; c = d) { - d = c->next; - - /* Detach child */ - c->parent = NULL; - - if (c->refcnt > 0) { - /* Something is using this attribute */ - - /** \todo add to list of nodes pending deletion */ - - continue; - } - - /* Detach from sibling list */ - c->previous = NULL; - c->next = NULL; + if (element->attributes != NULL) { + _dom_hash_destroy(element->attributes, _key, ctx, _value, ctx); + element->attributes = NULL; + } - dom_node_destroy(c); + if (element->ns_attributes != NULL) { + _dom_hash_destroy(element->ns_attributes, _key, ctx, + _nsattributes, ctx); + element->ns_attributes = NULL; } if (element->schema_type_info != NULL) { @@ -171,24 +245,15 @@ void dom_element_destroy(struct dom_document *doc, } /* Finalise base class */ - dom_node_finalise(doc, &element->base); + _dom_node_finalise(doc, &element->base); /* Free the element */ - dom_document_alloc(doc, element, 0); + _dom_document_alloc(doc, element, 0); } -/** - * The destroy virtual function of dom_element - * - * \param element The element to be destroyed - **/ -void _dom_element_destroy(struct dom_node_internal *node) -{ - struct dom_document *doc; - dom_node_get_owner_document(node, &doc); +/*----------------------------------------------------------------------*/ - dom_element_destroy(doc, (struct dom_element *) node); -} +/* The public virtual functions */ /** * Retrieve an element's tag name @@ -224,23 +289,7 @@ dom_exception _dom_element_get_tag_name(struct dom_element *element, dom_exception _dom_element_get_attribute(struct dom_element *element, struct dom_string *name, struct dom_string **value) { - struct dom_node_internal *a = (struct dom_node_internal *) - element->attributes; - - /* Search attributes, looking for name */ - for (; a != NULL; a = a->next) { - if (dom_string_cmp(a->name, name) == 0) - break; - } - - /* Fill in value */ - if (a == NULL) { - *value = NULL; - } else { - dom_attr_get_value(((struct dom_attr *) a), value); - } - - return DOM_NO_ERR; + return _dom_element_get_attr(element, element->attributes, name, value); } /** @@ -256,58 +305,7 @@ dom_exception _dom_element_get_attribute(struct dom_element *element, dom_exception _dom_element_set_attribute(struct dom_element *element, struct dom_string *name, struct dom_string *value) { - struct dom_node_internal *e = (struct dom_node_internal *) element; - struct dom_node_internal *a = (struct dom_node_internal *) - element->attributes; - - /** \todo validate name */ - - /* Ensure element can be written to */ - if (_dom_node_readonly(e)) - return DOM_NO_MODIFICATION_ALLOWED_ERR; - - /* Search for existing attribute with same name */ - for (; a != NULL; a = a->next) { - if (dom_string_cmp(a->name, name) == 0) - break; - } - - if (a != NULL) { - /* Found an existing attribute, so replace its value */ - dom_exception err; - - err = dom_attr_set_value((struct dom_attr *) a, value); - if (err != DOM_NO_ERR) - return err; - } else { - /* No existing attribute, so create one */ - dom_exception err; - struct dom_attr *attr; - - err = dom_attr_create(e->owner, name, NULL, NULL, &attr); - if (err != DOM_NO_ERR) - return err; - - /* Set its value */ - err = dom_attr_set_value(attr, value); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) attr); - return err; - } - - a = (struct dom_node_internal *) attr; - - /* And insert it into the element */ - a->previous = NULL; - a->next = (struct dom_node_internal *) element->attributes; - - if (a->next != NULL) - a->next->previous = a; - - element->attributes = attr; - } - - return DOM_NO_ERR; + return _dom_element_set_attr(element, element->attributes, name, value); } /** @@ -321,39 +319,7 @@ dom_exception _dom_element_set_attribute(struct dom_element *element, dom_exception _dom_element_remove_attribute(struct dom_element *element, struct dom_string *name) { - struct dom_node_internal *e = (struct dom_node_internal *) element; - struct dom_node_internal *a = (struct dom_node_internal *) - element->attributes; - - /* Ensure element can be written to */ - if (_dom_node_readonly(e)) - return DOM_NO_MODIFICATION_ALLOWED_ERR; - - /* Search for existing attribute with same name */ - for (; a != NULL; a = a->next) { - if (dom_string_cmp(a->name, name) == 0) - break; - } - - /* Detach attr node from list */ - if (a != NULL) { - if (a->previous != NULL) - a->previous->next = a->next; - else - element->attributes = (struct dom_attr *) a->next; - - if (a->next != NULL) - a->next->previous = a->previous; - - a->previous = a->next = a->parent = NULL; - - /* And destroy attr */ - dom_node_unref(a); - } - - /** \todo defaulted attribute handling */ - - return DOM_NO_ERR; + return _dom_element_remove_attr(element, element->attributes, name); } /** @@ -368,23 +334,11 @@ dom_exception _dom_element_remove_attribute(struct dom_element *element, * the responsibility of the caller to unref the node once it has * finished with it. */ -dom_exception _dom_element_get_attribute_node(struct dom_element *element, +dom_exception _dom_element_get_attribute_node(struct dom_element *element, struct dom_string *name, struct dom_attr **result) { - struct dom_node_internal *a = (struct dom_node_internal *) - element->attributes; - - /* Search attributes, looking for name */ - for (; a != NULL; a = a->next) { - if (dom_string_cmp(a->name, name) == 0) - break; - } - - if (a != NULL) - dom_node_ref(a); - *result = (struct dom_attr *) a; - - return DOM_NO_ERR; + return _dom_element_get_attr_node(element, element->attributes, name, + result); } /** @@ -407,78 +361,8 @@ dom_exception _dom_element_get_attribute_node(struct dom_element *element, dom_exception _dom_element_set_attribute_node(struct dom_element *element, struct dom_attr *attr, struct dom_attr **result) { - struct dom_node_internal *e = (struct dom_node_internal *) element; - struct dom_node_internal *a = (struct dom_node_internal *) attr; - struct dom_attr *prev = NULL; - - /* Ensure element and attribute belong to the same document */ - if (e->owner != a->owner) - return DOM_WRONG_DOCUMENT_ERR; - - /* Ensure element can be written to */ - if (_dom_node_readonly(e)) - return DOM_NO_MODIFICATION_ALLOWED_ERR; - - /* Ensure attribute isn't attached to another element */ - if (a->parent != NULL && a->parent != e) - return DOM_INUSE_ATTRIBUTE_ERR; - - /* Attach attr to element, if not already attached */ - if (a->parent == NULL) { - - /* Search for existing attribute with same name */ - prev = element->attributes; - while (prev != NULL) { - struct dom_node_internal *p = - (struct dom_node_internal *) prev; - - if (dom_string_cmp(a->name, p->name) == 0) - break; - - prev = (struct dom_attr *) p->next; - } - - a->parent = e; - - if (prev != NULL) { - /* Found an existing attribute, so replace it */ - struct dom_node_internal *p = - (struct dom_node_internal *) prev; - - a->previous = p->previous; - a->next = p->next; - - if (a->previous != NULL) - a->previous->next = a; - else - element->attributes = attr; - - if (a->next != NULL) - a->next->previous = a; - - /* Invalidate existing attribute's location info */ - p->next = NULL; - p->previous = NULL; - p->parent = NULL; - } else { - /* No existing attribute, so insert at front of list */ - a->previous = NULL; - a->next = (struct dom_node_internal *) - element->attributes; - - if (a->next != NULL) - a->next->previous = a; - - element->attributes = attr; - } - } - - if (prev != NULL) - dom_node_ref((struct dom_node *) prev); - - *result = prev; - - return DOM_NO_ERR; + return _dom_element_set_attr_node(element, element->attributes, attr, + result); } /** @@ -499,35 +383,8 @@ dom_exception _dom_element_set_attribute_node(struct dom_element *element, dom_exception _dom_element_remove_attribute_node(struct dom_element *element, struct dom_attr *attr, struct dom_attr **result) { - struct dom_node_internal *e = (struct dom_node_internal *) element; - struct dom_node_internal *a = (struct dom_node_internal *) attr; - - /* Ensure element can be written to */ - if (_dom_node_readonly(e)) - return DOM_NO_MODIFICATION_ALLOWED_ERR; - - /* Ensure attr is an attribute of element */ - if (a->parent != e) - return DOM_NOT_FOUND_ERR; - - /* Detach attr node from list */ - if (a->previous != NULL) - a->previous->next = a->next; - else - element->attributes = (struct dom_attr *) a->next; - - if (a->next != NULL) - a->next->previous = a->previous; - - a->previous = a->next = a->parent = NULL; - - /** \todo defaulted attribute handling */ - - /* Return the detached node */ - dom_node_ref(a); - *result = attr; - - return DOM_NO_ERR; + return _dom_element_remove_attr_node(element, element->attributes, + attr, result); } /** @@ -547,9 +404,25 @@ dom_exception _dom_element_get_elements_by_tag_name( struct dom_element *element, struct dom_string *name, struct dom_nodelist **result) { - return dom_document_get_nodelist(element->base.owner, - (struct dom_node_internal *) element, name, NULL, + dom_exception err; + lwc_string *n; + lwc_context *ctx; + dom_node_internal *base = (dom_node_internal *) element; + + assert(base->owner != NULL); + ctx = _dom_document_get_intern_context(base->owner); + assert(ctx != NULL); + + err = _dom_string_intern(name, ctx, &n); + if (err != DOM_NO_ERR) + return err; + + err = _dom_document_get_nodelist(base->owner, DOM_NODELIST_BY_NAME, + (struct dom_node_internal *) element, n, NULL, NULL, result); + + lwc_context_string_unref(ctx, n); + return err; } /** @@ -573,28 +446,26 @@ dom_exception _dom_element_get_attribute_ns(struct dom_element *element, struct dom_string *namespace, struct dom_string *localname, struct dom_string **value) { - struct dom_node_internal *a = (struct dom_node_internal *) - element->attributes; - - /** \todo ensure implementation supports XML */ - - /* Search attributes, looking for namespace/localname pair */ - for (; a != NULL; a = a->next) { - if (((namespace == NULL && a->namespace == NULL) || - (namespace != NULL && - dom_string_cmp(a->namespace, namespace) == 0)) && - dom_string_cmp(a->name, localname) == 0) - break; - } + lwc_string *str; + dom_exception err; + struct dom_hash_table *attrs; - /* Fill in value */ - if (a == NULL) { + if (namespace == NULL) + return _dom_element_get_attribute(element, localname, value); + + err = _dom_node_get_intern_string(&element->base, namespace, &str); + if (err != DOM_NO_ERR) + return err; + + attrs = (struct dom_hash_table *) _dom_hash_get(element->ns_attributes, + str); + /* The element has no such namespace */ + if (attrs == NULL) { *value = NULL; - } else { - dom_attr_get_value(((struct dom_attr *) a), value); + return DOM_NO_ERR; } - return DOM_NO_ERR; + return _dom_element_get_attr(element, attrs, localname, value); } /** @@ -630,101 +501,56 @@ dom_exception _dom_element_set_attribute_ns(struct dom_element *element, struct dom_string *namespace, struct dom_string *qname, struct dom_string *value) { - struct dom_node_internal *e = (struct dom_node_internal *) element; - struct dom_node_internal *a = (struct dom_node_internal *) - element->attributes; - struct dom_string *prefix, *localname; + lwc_string *str; dom_exception err; + struct dom_hash_table *attrs; + bool added; - /** \todo ensure XML feature is supported */ + if (_dom_validate_name(qname) == false) + return DOM_INVALID_CHARACTER_ERR; - /* Validate name */ err = _dom_namespace_validate_qname(qname, namespace); - if (err != DOM_NO_ERR) { - return err; - } + if (err != DOM_NO_ERR) + return DOM_NAMESPACE_ERR; - /* Ensure element can be written to */ - if (_dom_node_readonly(e)) { - return DOM_NO_MODIFICATION_ALLOWED_ERR; - } - - /* Decompose QName */ + dom_string *localname; + dom_string *prefix; err = _dom_namespace_split_qname(qname, &prefix, &localname); - if (err != DOM_NO_ERR) { + if (err != DOM_NO_ERR) return err; - } - /* Search for existing attribute with same namespace/localname */ - for (; a != NULL; a = a->next) { - if (((namespace == NULL && a->namespace == NULL) || - (namespace != NULL && - dom_string_cmp(a->namespace, namespace) == 0)) && - dom_string_cmp(a->name, localname) == 0) - break; + /* If there is no namespace, redirect to set_attribute */ + if (namespace == NULL) { + if (prefix != NULL) + return DOM_NAMESPACE_ERR; + err = _dom_element_set_attribute(element, localname, value); + dom_string_unref(localname); + return err; } - if (a != NULL) { - /* Found an existing attribute, so replace its prefix & value */ - dom_exception err; - - err = dom_node_set_prefix(a, prefix); - if (err != DOM_NO_ERR) { - if (prefix != NULL) { - dom_string_unref(prefix); - } - dom_string_unref(localname); - return err; - } - - err = dom_attr_set_value((struct dom_attr *) a, value); - if (err != DOM_NO_ERR) { - if (prefix != NULL) { - dom_string_unref(prefix); - } - dom_string_unref(localname); - return err; - } - } else { - /* No existing attribute, so create one */ - dom_exception err; - struct dom_attr *attr; - - err = dom_attr_create(e->owner, localname, - namespace, prefix, &attr); - if (err != DOM_NO_ERR) { - if (prefix != NULL) { - dom_string_unref(prefix); - } - dom_string_unref(localname); - return err; - } - - /* Set its value */ - err = dom_attr_set_value(attr, value); - if (err != DOM_NO_ERR) { - dom_node_unref((struct dom_node *) attr); - - if (prefix != NULL) { - dom_string_unref(prefix); - } - dom_string_unref(localname); + err = _dom_node_get_intern_string(&element->base, namespace, &str); + if (err != DOM_NO_ERR) + return err; + + attrs = (struct dom_hash_table *) _dom_hash_get(element->ns_attributes, + str); + /* The element has no such namespace */ + if (attrs == NULL) { + dom_document *doc; + doc = dom_node_get_owner(element); + assert(doc != NULL); + err = _dom_document_create_hashtable(doc, CHAINS_NS_ATTRIBUTES, + _dom_element_hash_lwcstring, &attrs); + if (err != DOM_NO_ERR) return err; - } - a = (struct dom_node_internal *) attr; - - /* And insert it into the element */ - a->previous = NULL; - a->next = (struct dom_node_internal *) element->attributes; - - if (a->next != NULL) - a->next->previous = a; - - element->attributes = attr; + added = _dom_hash_add(element->ns_attributes, str, attrs, + false); + if (added == false) + return DOM_NO_MEM_ERR; } - return DOM_NO_ERR; + return _dom_element_set_attr(element, attrs, localname, value); } /** @@ -744,44 +570,25 @@ dom_exception _dom_element_set_attribute_ns(struct dom_element *element, dom_exception _dom_element_remove_attribute_ns(struct dom_element *element, struct dom_string *namespace, struct dom_string *localname) { - struct dom_node_internal *e = (struct dom_node_internal *) element; - struct dom_node_internal *a = (struct dom_node_internal *) - element->attributes; - - /** \todo ensure XML feature is supported */ - - /* Ensure element can be written to */ - if (_dom_node_readonly(e)) - return DOM_NO_MODIFICATION_ALLOWED_ERR; - - /* Search for existing attribute with same namespace/localname */ - for (; a != NULL; a = a->next) { - if (((namespace == NULL && a->namespace == NULL) || - (namespace != NULL && - dom_string_cmp(a->namespace, namespace) == 0)) && - dom_string_cmp(a->name, localname) == 0) - break; - } - - /* Detach attr node from list */ - if (a != NULL) { - if (a->previous != NULL) - a->previous->next = a->next; - else - element->attributes = (struct dom_attr *) a->next; - - if (a->next != NULL) - a->next->previous = a->previous; + lwc_string *str; + dom_exception err; + struct dom_hash_table *attrs; - a->previous = a->next = a->parent = NULL; + if (namespace != NULL) + return _dom_element_remove_attribute(element, localname); - /* And destroy attr */ - dom_node_unref(a); + err = _dom_node_get_intern_string(&element->base, namespace, &str); + if (err != DOM_NO_ERR) + return err; + + attrs = (struct dom_hash_table *) _dom_hash_get(element->ns_attributes, + str); + /* The element has no such namespace */ + if (attrs == NULL) { + return DOM_NO_ERR; } - /** \todo defaulted attribute handling */ - - return DOM_NO_ERR; + return _dom_element_remove_attr(element, attrs, localname); } /** @@ -805,25 +612,28 @@ dom_exception _dom_element_get_attribute_node_ns(struct dom_element *element, struct dom_string *namespace, struct dom_string *localname, struct dom_attr **result) { - struct dom_node_internal *a = (struct dom_node_internal *) - element->attributes; - - /** \todo ensure XML feature is supported */ + lwc_string *str; + dom_exception err; + struct dom_hash_table *attrs; - /* Search attributes, looking for namespace/localname */ - for (; a != NULL; a = a->next) { - if (((namespace == NULL && a->namespace == NULL) || - (namespace != NULL && - dom_string_cmp(a->namespace, namespace) == 0)) && - dom_string_cmp(a->name, localname) == 0) - break; + if (namespace == NULL) { + return _dom_element_get_attribute_node(element, localname, + result); } - if (a != NULL) - dom_node_ref(a); - *result = (struct dom_attr *) a; + err = _dom_node_get_intern_string(&element->base, namespace, &str); + if (err != DOM_NO_ERR) + return err; + + attrs = (struct dom_hash_table *) _dom_hash_get(element->ns_attributes, + str); + /* The element has no such namespace */ + if (attrs == NULL) { + *result = NULL; + return DOM_NO_ERR; + } - return DOM_NO_ERR; + return _dom_element_get_attr_node(element, attrs, localname, result); } /** @@ -851,84 +661,47 @@ dom_exception _dom_element_get_attribute_node_ns(struct dom_element *element, dom_exception _dom_element_set_attribute_node_ns(struct dom_element *element, struct dom_attr *attr, struct dom_attr **result) { - struct dom_node_internal *e = (struct dom_node_internal *) element; - struct dom_node_internal *a = (struct dom_node_internal *) attr; - struct dom_attr *prev = NULL; - - /** \todo ensure XML feature is supported */ - - /* Ensure element and attribute belong to the same document */ - if (e->owner != a->owner) - return DOM_WRONG_DOCUMENT_ERR; - - /* Ensure element can be written to */ - if (_dom_node_readonly(e)) - return DOM_NO_MODIFICATION_ALLOWED_ERR; - - /* Ensure attribute isn't attached to another element */ - if (a->parent != NULL && a->parent != e) - return DOM_INUSE_ATTRIBUTE_ERR; - - /* Attach attr to element, if not already attached */ - if (a->parent == NULL) { - - /* Search for existing attribute with same namespace/localname */ - prev = element->attributes; - while (prev != NULL) { - struct dom_node_internal *p = - (struct dom_node_internal *) prev; - - if (((a->namespace == NULL && p->namespace == NULL) || - (a->namespace != NULL && - dom_string_cmp(a->namespace, - p->namespace) == 0)) && - dom_string_cmp(a->name, p->name) == 0) - break; - - prev = (struct dom_attr *) p->next; - } - - a->parent = e; - - if (prev != NULL) { - /* Found an existing attribute, so replace it */ - struct dom_node_internal *p = - (struct dom_node_internal *) prev; - - a->previous = p->previous; - a->next = p->next; - - if (a->previous != NULL) - a->previous->next = a; - else - element->attributes = attr; + lwc_string *str; + dom_exception err; + struct dom_hash_table *attrs; + bool added; + dom_string *namespace; - if (a->next != NULL) - a->next->previous = a; + err = dom_node_get_namespace(attr, (void *) &namespace); + if (err != DOM_NO_ERR) + return err; - /* Invalidate existing attribute's location info */ - p->next = NULL; - p->previous = NULL; - p->parent = NULL; - } else { - /* No existing attribute, so insert at front of list */ - a->previous = NULL; - a->next = (struct dom_node_internal *) - element->attributes; + if (namespace == NULL) + return _dom_element_set_attribute_node(element, attr, result); - if (a->next != NULL) - a->next->previous = a; + err = _dom_node_get_intern_string(&element->base, namespace, &str); + if (err != DOM_NO_ERR) + return err; + + attrs = (struct dom_hash_table *) _dom_hash_get(element->ns_attributes, + str); + /* The element has no such namespace */ + if (attrs == NULL) { + dom_document *doc; + doc = dom_node_get_owner(element); + assert(doc != NULL); + err = _dom_document_create_hashtable(doc, CHAINS_NS_ATTRIBUTES, + _dom_element_hash_lwcstring, &attrs); + if (err != DOM_NO_ERR) + return err; - element->attributes = attr; - } + added = _dom_hash_add(element->ns_attributes, str, attrs, + false); + if (added == false) + return DOM_NO_MEM_ERR; } - if (prev != NULL) - dom_node_ref((struct dom_node *) prev); - - *result = prev; + dom_string *localname; + err = dom_node_get_local_name(attr, (void *) &localname); + if (err != DOM_NO_ERR) + return err; - return DOM_NO_ERR; + return _dom_element_set_attr_node(element, attrs, attr, result); } /** @@ -953,11 +726,40 @@ dom_exception _dom_element_get_elements_by_tag_name_ns( struct dom_element *element, struct dom_string *namespace, struct dom_string *localname, struct dom_nodelist **result) { + dom_document *doc; + dom_exception err; + doc = element->base.owner; + /** \todo ensure XML feature is supported */ - return dom_document_get_nodelist(element->base.owner, - (struct dom_node_internal *) element, NULL, - namespace, localname, result); + /* Get the interned string from the dom_string */ + assert(doc->context != NULL); + lwc_string *l = NULL, *n = NULL; + if (localname != NULL) { + err = _dom_string_intern(localname, doc->context, &l); + if (err != DOM_NO_ERR) + return err; + } + if (namespace != NULL) { + err = _dom_string_intern(namespace, doc->context, &n); + if (err != DOM_NO_ERR) { + lwc_context_string_unref(doc->context, l); + + return err; + } + } + + err = _dom_document_get_nodelist(element->base.owner, + DOM_NODELIST_BY_NAMESPACE, + (struct dom_node_internal *) element, NULL, n, l, + result); + + if (localname != NULL) + lwc_context_string_unref(doc->context, l); + if (namespace != NULL) + lwc_context_string_unref(doc->context, n); + + return err; } /** @@ -971,18 +773,8 @@ dom_exception _dom_element_get_elements_by_tag_name_ns( dom_exception _dom_element_has_attribute(struct dom_element *element, struct dom_string *name, bool *result) { - struct dom_node_internal *a = (struct dom_node_internal *) - element->attributes; - - /* Search attributes, looking for name */ - for (; a != NULL; a = a->next) { - if (dom_string_cmp(a->name, name) == 0) - break; - } - - *result = (a != NULL); - - return DOM_NO_ERR; + return _dom_element_has_attr(element, element->attributes, name, + result); } /** @@ -1003,23 +795,26 @@ dom_exception _dom_element_has_attribute_ns(struct dom_element *element, struct dom_string *namespace, struct dom_string *localname, bool *result) { - struct dom_node_internal *a = (struct dom_node_internal *) - element->attributes; + lwc_string *str; + dom_exception err; + struct dom_hash_table *attrs; - /** \todo ensure XML feature is supported */ + if (namespace == NULL) + return _dom_element_has_attribute(element, localname, result); - /* Search attributes, looking for namespace/localname */ - for (; a != NULL; a = a->next) { - if (((namespace == NULL && a->namespace == NULL) || - (namespace != NULL && - dom_string_cmp(a->namespace, namespace) == 0)) && - dom_string_cmp(a->name, localname) == 0) - break; + err = _dom_node_get_intern_string(&element->base, namespace, &str); + if (err != DOM_NO_ERR) + return err; + + attrs = (struct dom_hash_table *) _dom_hash_get(element->ns_attributes, + str); + /* The element has no such namespace */ + if (attrs == NULL) { + *result = false; + return DOM_NO_ERR; } - *result = (a != NULL); - - return DOM_NO_ERR; + return _dom_element_has_attr(element, attrs, localname, result); } /** @@ -1052,15 +847,16 @@ dom_exception _dom_element_get_schema_type_info(struct dom_element *element, * DOM_NO_MODIFICATION_ALLOWED_ERR if ::element is readonly, * DOM_NOT_FOUND_ERR if the specified node is not an * attribute of ::element. + * + * @note: The DOM spec does not say: how to deal with when there are two or + * more isId attribute nodes. Here, the implementation just maintain only + * one such attribute node. */ dom_exception _dom_element_set_id_attribute(struct dom_element *element, struct dom_string *name, bool is_id) { - UNUSED(element); - UNUSED(name); - UNUSED(is_id); - - return DOM_NOT_SUPPORTED_ERR; + return _dom_element_set_id_attr(element, element->attributes, name, + is_id); } /** @@ -1079,12 +875,28 @@ dom_exception _dom_element_set_id_attribute_ns(struct dom_element *element, struct dom_string *namespace, struct dom_string *localname, bool is_id) { - UNUSED(element); - UNUSED(namespace); - UNUSED(localname); - UNUSED(is_id); + struct dom_hash_table *hs; + dom_exception err; + lwc_string *ns; - return DOM_NOT_SUPPORTED_ERR; + if (namespace == NULL) + return _dom_element_set_id_attribute(element, localname, is_id); + + err = _dom_node_get_intern_string(&element->base, namespace, &ns); + if (err != DOM_NO_ERR) + return err; + + hs = (struct dom_hash_table *) _dom_hash_get(element->ns_attributes, + ns); + assert(hs != NULL); + + err = _dom_element_set_id_attr(element, hs, localname, is_id); + if (err != DOM_NO_ERR) + return err; + + element->id_ns = ns; + + return DOM_NO_ERR; } /** @@ -1101,59 +913,1078 @@ dom_exception _dom_element_set_id_attribute_ns(struct dom_element *element, dom_exception _dom_element_set_id_attribute_node(struct dom_element *element, struct dom_attr *id_attr, bool is_id) { - UNUSED(element); - UNUSED(id_attr); - UNUSED(is_id); + struct dom_hash_table *hs; + dom_exception err; + lwc_string *ns; + dom_string *namespace; + dom_string *localname; - return DOM_NOT_SUPPORTED_ERR; + err = dom_node_get_namespace(id_attr, &namespace); + if (err != DOM_NO_ERR) + return err; + err = dom_node_get_local_name(id_attr, &localname); + if (err != DOM_NO_ERR) + return err; + + err = _dom_node_get_intern_string(&element->base, namespace, &ns); + if (err != DOM_NO_ERR) + return err; + + hs = (struct dom_hash_table *) _dom_hash_get(element->ns_attributes, + ns); + assert(hs != NULL); + + err = _dom_element_set_id_attr(element, hs, localname, is_id); + if (err != DOM_NO_ERR) + return err; + + element->id_ns = ns; + + return DOM_NO_ERR; + +} + +/*------------- The overload virtual functions ------------------------*/ + +/* Overload function of Node, please refer src/core/node.c for detail */ +dom_exception _dom_element_get_attributes(dom_node_internal *node, + struct dom_namednodemap **result) +{ + dom_exception err; + dom_document *doc; + + doc = dom_node_get_owner(node); + assert(doc != NULL); + + err = _dom_namednodemap_create(doc, node, &attributes_opt, result); + if (err != DOM_NO_ERR) + return err; + + dom_node_ref(node); + + return DOM_NO_ERR; } -/* */ -/*----------------------------------------------------------------------------*/ -/* */ +/* Overload function of Node, please refer src/core/node.c for detail */ +dom_exception _dom_element_has_attributes(dom_node_internal *node, bool *result) +{ + UNUSED(node); + *result = true; + + return DOM_NO_ERR; +} + +/* For the following namespace related algorithm take a look at: + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html + */ /** - * Retrieve a map of attributes associated with an Element + * Look up the prefix which matches the namespace. * - * \param element The element to retrieve the attributes of - * \param result Pointer to location to receive attribute map - * \return DOM_NO_ERR. + * \param node The current Node in which we search for + * \param namespace The namespace for which we search a prefix + * \param result The returned prefix + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_element_lookup_prefix(dom_node_internal *node, + struct dom_string *namespace, struct dom_string **result) +{ + struct dom_element *owner; + dom_exception err; + + err = dom_attr_get_owner_element(node, &owner); + if (err != DOM_NO_ERR) + return err; + + if (owner == NULL) { + *result = NULL; + return DOM_NO_ERR; + } + + return dom_node_lookup_prefix(owner, namespace, result); +} + +/** + * Test whether certain namespace is the default namespace of some node. * - * The returned NamedNodeMap will be referenced. It is the responsibility - * of the caller to unref the map once it has finished with it. + * \param node The Node to test + * \param namespace The namespace to test + * \param result true is the namespace is default namespace + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. */ -dom_exception dom_element_get_attributes(struct dom_element *element, - struct dom_namednodemap **result) +dom_exception _dom_element_is_default_namespace(dom_node_internal *node, + struct dom_string *namespace, bool *result) { - return dom_document_get_namednodemap(element->base.owner, - (struct dom_node_internal *) element, - DOM_ATTRIBUTE_NODE, result); + struct dom_element *ele = (struct dom_element *) node; + lwc_string *ns; + dom_string *value; + dom_document *doc = node->owner; + lwc_context *ctx; + dom_exception err; + + assert(doc != NULL); + err = _dom_node_get_intern_string(node, namespace, &ns); + if (err != DOM_NO_ERR) { + return err; + } + ctx = _dom_document_get_intern_context(doc); + assert(ctx != NULL); + if (node->prefix == NULL) { + lwc_context_string_isequal(ctx, node->namespace, ns, result); + lwc_context_string_unref(ctx, ns); + return DOM_NO_ERR; + } + + bool has; + dom_string *xmlns = _dom_namespace_get_xmlns_prefix(); + err = dom_element_has_attribute(ele, xmlns, &has); + if (err != DOM_NO_ERR) + return err; + + if (has == true) { + return dom_element_get_attribute(ele, xmlns, &value); + } + + lwc_string *ns2; + err = _dom_node_get_intern_string(node, value, &ns2); + if (err != DOM_NO_ERR) { + return err; + } + + if (ns2 != NULL) { + lwc_context_string_isequal(ctx, ns2, ns, result); + lwc_context_string_unref(ctx, ns); + lwc_context_string_unref(ctx, ns2); + dom_string_unref(value); + return DOM_NO_ERR; + } + + + return dom_node_is_default_namespace(node->parent, namespace, result); } /** - * Determine if an element has any attributes + * Look up the namespace with certain prefix. * - * \param element Element to inspect - * \param result Pointer to location to receive result + * \param node The current node in which we search for the prefix + * \param prefix The prefix to search + * \param result The result namespace if found + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_element_lookup_namespace(dom_node_internal *node, + struct dom_string *prefix, struct dom_string **result) +{ + lwc_string *pf; + dom_exception err; + + err = _dom_node_get_intern_string(node, prefix, &pf); + if (err != DOM_NO_ERR) + return err; + + /* To some extent, directly compare the two lwc_string pointer + * is better */ + if (node->namespace != NULL && node->prefix == pf) { + assert(node->owner != NULL); + return _dom_document_create_string_from_lwcstring(node->owner, + pf, result); + } + + bool has; + dom_string *xmlns = _dom_namespace_get_xmlns_prefix(); + err = dom_element_has_attribute_ns(node, + dom_namespaces[DOM_NAMESPACE_XMLNS], prefix, &has); + if (err != DOM_NO_ERR) + return err; + + if (has == true) + return dom_element_get_attribute_ns(node, + dom_namespaces[DOM_NAMESPACE_XMLNS], prefix, + result); + + err = dom_element_has_attribute(node, xmlns, &has); + if (err != DOM_NO_ERR) + return err; + + if (has == true) { + return dom_element_get_attribute(node, xmlns, result); + } + + return dom_node_lookup_namespace(node->parent, prefix, result); +} + + +/*----------------------------------------------------------------------*/ +/* The protected virtual functions */ + +/* The destroy virtual function of dom_element */ +void __dom_element_destroy(struct dom_node_internal *node) +{ + struct dom_document *doc = dom_node_get_owner(node); + + _dom_element_destroy(doc, (struct dom_element *) node); +} + +/* The memory allocator of this class */ +dom_exception _dom_element_alloc(dom_document *doc, struct dom_node_internal *n, + struct dom_node_internal **ret) +{ + dom_element *e; + UNUSED(n); + + e = _dom_document_alloc(doc, NULL, sizeof(struct dom_element)); + if (e == NULL) + return DOM_NO_MEM_ERR; + + *ret = (dom_node_internal *) e; + dom_node_set_owner(*ret, doc); + + return DOM_NO_ERR; +} + +/* TODO: How to deal with default attribue: + * + * Ask a language binding for default attributes. + * + * So, when we copy a element we copy all its attributes because they + * are all specified. For the methods like importNode and adoptNode, + * this will make _dom_element_copy can be used in them. + */ +dom_exception _dom_element_copy(struct dom_node_internal *new, + struct dom_node_internal *old) +{ + dom_element *ne = (dom_element *) new; + dom_element *oe = (dom_element *) old; + dom_document *od, *nd; + struct dom_hash_table *ht; + lwc_context *oc, *nc; + dom_exception err; + + err = _dom_node_copy(new, old); + if (err != DOM_NO_ERR) + return err; + + od = dom_node_get_owner(old); + nd = dom_node_get_owner(new); + assert(od != NULL); + assert(nd != NULL); + + oc = _dom_document_get_intern_context(od); + nc = _dom_document_get_intern_context(nd); + assert(oc != NULL); + assert(nc != NULL); + + dom_alloc alloc; + void *pw; + _dom_document_get_allocator(nd, &alloc, &pw); + + /* Copy the hash tables */ + ht = _dom_hash_clone(oe->attributes, alloc, pw, _key, nc, + _value, nd); + if (ht == NULL) + return DOM_NO_MEM_ERR; + ne->attributes = ht; + + ht = _dom_hash_clone(oe->ns_attributes, alloc, pw, _key, nc, + _nsattributes, nd); + if (ht == NULL) + return DOM_NO_MEM_ERR; + ne->ns_attributes = ht; + + /* TODO: deal with dom_type_info, it get no definition ! */ + + return DOM_NO_ERR; +} + + + +/*--------------------------------------------------------------------------*/ + +/* Helper functions */ + +/** + * The internal helper function for getAttribute/getAttributeNS. + * + * \param element The element + * \param hs The hash table contains the attributes + * \param name The name of the attribute + * \param value The value of the attribute + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_element_get_attr(struct dom_element *element, + struct dom_hash_table *hs, struct dom_string *name, + struct dom_string **value) +{ + void *a; + dom_exception err; + lwc_string *str; + + /* Looking for name */ + err = _dom_node_get_intern_string(&element->base, name, &str); + if (err != DOM_NO_ERR) + return err; + + a = _dom_hash_get(hs, str); + + /* Fill in value */ + if (a == NULL) { + *value = NULL; + } else { + dom_attr_get_value(((struct dom_attr *) a), value); + } + + return DOM_NO_ERR; +} + +/** + * The internal helper function for setAttribute and setAttributeNS. + * + * \param element The element + * \param hs The attributes' hash table + * \param name The name of the new attribute + * \param value The value of the new attribute + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_element_set_attr(struct dom_element *element, + struct dom_hash_table *hs, struct dom_string *name, + struct dom_string *value) +{ + void *a; + dom_exception err; + lwc_string *str; + bool added; + dom_node_internal *e = (dom_node_internal *) element; + + if (_dom_validate_name(name) == false) + return DOM_INVALID_CHARACTER_ERR; + + /* Ensure element can be written */ + if (_dom_node_readonly(e)) + return DOM_NO_MODIFICATION_ALLOWED_ERR; + + /* Looking for name */ + err = _dom_node_get_intern_string(&element->base, name, &str); + if (err != DOM_NO_ERR) + return err; + + a = _dom_hash_get(hs, str); + + if (a != NULL) { + /* Found an existing attribute, so replace its value */ + dom_exception err; + + err = dom_attr_set_value((struct dom_attr *) a, value); + if (err != DOM_NO_ERR) + return err; + } else { + /* No existing attribute, so create one */ + dom_exception err; + struct dom_attr *attr; + + err = _dom_attr_create(e->owner, str, NULL, NULL, true, &attr); + if (err != DOM_NO_ERR) + return err; + + /* Set its value */ + err = dom_attr_set_value(attr, value); + if (err != DOM_NO_ERR) { + dom_node_unref(attr); + return err; + } + + added = _dom_hash_add(hs, str, attr, false); + if (added == false) { + /* If we failed at this step, there must be no memory */ + dom_node_unref(attr); + return DOM_NO_MEM_ERR; + } + + dom_node_set_parent(attr, element); + dom_node_unref(attr); + dom_node_remove_pending(attr); + } + + return DOM_NO_ERR; +} + +/** + * Remove an attribute from an element by name + * + * \param element The element to remove attribute from + * \param name The name of the attribute to remove + * \return DOM_NO_ERR on success, + * DOM_NO_MODIFICATION_ALLOWED_ERR if ::element is readonly. + */ +dom_exception _dom_element_remove_attr(struct dom_element *element, + struct dom_hash_table *hs, struct dom_string *name) +{ + void *a; + dom_exception err; + lwc_string *str; + dom_node_internal *e = (dom_node_internal *) element; + + /* Ensure element can be written to */ + if (_dom_node_readonly(e)) + return DOM_NO_MODIFICATION_ALLOWED_ERR; + + /* Looking for name */ + err = _dom_node_get_intern_string(&element->base, name, &str); + if (err != DOM_NO_ERR) + return err; + + a = (dom_node_internal *) _dom_hash_del(hs, str); + + /* Detach attr node from list */ + if (a != NULL) { + /* And destroy attr */ + dom_node_set_parent(a, NULL); + dom_node_try_destroy(a); + } + + /** \todo defaulted attribute handling */ + + return DOM_NO_ERR; +} + +/** + * Retrieve an attribute node from an element by name + * + * \param element The element to retrieve attribute node from + * \param name The attribute's name + * \param result Pointer to location to receive attribute node * \return DOM_NO_ERR. + * + * The returned node will have its reference count increased. It is + * the responsibility of the caller to unref the node once it has + * finished with it. + */ +dom_exception _dom_element_get_attr_node(struct dom_element *element, + struct dom_hash_table *hs, struct dom_string *name, + struct dom_attr **result) +{ + void *a; + dom_exception err; + lwc_string *str; + + /* Looking for name */ + err = _dom_node_get_intern_string(&element->base, name, &str); + if (err != DOM_NO_ERR) + return err; + + a = _dom_hash_get(hs, str); + + /* Fill in value */ + if (a == NULL) { + *result = NULL; + } else { + *result = (dom_attr *) a; + dom_node_ref(*result); + } + + return DOM_NO_ERR; +} + +/** + * Set an attribute node on an element, replacing existing node, if present + * + * \param element The element to add a node to + * \param attr The attribute node to add + * \param result Pointer to location to receive previous node + * \return DOM_NO_ERR on success, + * DOM_WRONG_DOCUMENT_ERR if ::attr does not belong to the + * same document as ::element, + * DOM_NO_MODIFICATION_ALLOWED_ERR if ::element is readonly, + * DOM_INUSE_ATTRIBUTE_ERR if ::attr is already an attribute + * of another Element node. + * + * The returned node will have its reference count increased. It is + * the responsibility of the caller to unref the node once it has + * finished with it. */ -dom_exception dom_element_has_attributes(struct dom_element *element, +dom_exception _dom_element_set_attr_node(struct dom_element *element, + struct dom_hash_table *hs, struct dom_attr *attr, + struct dom_attr **result) +{ + dom_exception err; + lwc_string *str = NULL; + dom_string *name = NULL; + bool added; + dom_node_internal *e = (dom_node_internal *) element; + dom_node_internal *a = (dom_node_internal *) attr; + + /** \todo validate name */ + + /* Ensure element and attribute belong to the same document */ + if (e->owner != a->owner) + return DOM_WRONG_DOCUMENT_ERR; + + /* Ensure element can be written to */ + if (_dom_node_readonly(e)) + return DOM_NO_MODIFICATION_ALLOWED_ERR; + + /* Ensure attribute isn't attached to another element */ + if (a->parent != NULL && a->parent != e) + return DOM_INUSE_ATTRIBUTE_ERR; + + err = dom_node_get_local_name(attr, &name); + if (err != DOM_NO_ERR) + return err; + + /* Looking for name */ + err = _dom_node_get_intern_string(&element->base, name, &str); + if (err != DOM_NO_ERR) + return err; + + a = _dom_hash_del(hs, str); + + *result = NULL; + if (a != NULL) { + dom_node_ref(a); + *result = (dom_attr *) a; + dom_node_set_parent(a, NULL); + dom_node_mark_pending(a); + } + + added = _dom_hash_add(hs, str, attr, false); + if (added == false) { + /* If we failed at this step, there must be no memory */ + return DOM_NO_MEM_ERR; + } + dom_node_set_parent(attr, element); + dom_node_remove_pending(attr); + + /* Cleanup */ + if (name != NULL) + dom_string_unref(name); + + return DOM_NO_ERR; +} + +/** + * Remove an attribute node from an element + * + * \param element The element to remove attribute node from + * \param attr The attribute node to remove + * \param result Pointer to location to receive attribute node + * \return DOM_NO_ERR on success, + * DOM_NO_MODIFICATION_ALLOWED_ERR if ::element is readonly, + * DOM_NOT_FOUND_ERR if ::attr is not an attribute of + * ::element. + * + * The returned node will have its reference count increased. It is + * the responsibility of the caller to unref the node once it has + * finished with it. + */ +dom_exception _dom_element_remove_attr_node(struct dom_element *element, + struct dom_hash_table *hs, struct dom_attr *attr, + struct dom_attr **result) +{ + void *a; + dom_exception err; + lwc_string *str; + dom_string *name; + dom_node_internal *e = (dom_node_internal *) element; + + /* Ensure element can be written to */ + if (_dom_node_readonly(e)) + return DOM_NO_MODIFICATION_ALLOWED_ERR; + + err = dom_node_get_node_name(attr, &name); + if (err != DOM_NO_ERR) + return err; + + /* Looking for name */ + err = _dom_node_get_intern_string(&element->base, name, &str); + if (err != DOM_NO_ERR) + return err; + + a = _dom_hash_del(hs, str); + + /* Now, cleaup the dom_string and lwc_string */ + dom_string_unref(name); + _dom_node_unref_intern_string(&element->base, str); + + /** \todo defaulted attribute handling */ + + if (a == NULL || a != (void *) attr) { + return DOM_NOT_FOUND_ERR; + } + + /* When a Node is removed, it should be destroy. When its refcnt is not + * zero, it will be added to the document's deletion pending list. + * When a Node is removed, its parent should be NULL, but its owner + * should remain to be the document. + */ + dom_node_ref(a); + *result = (dom_attr *) a; + dom_node_set_parent(a, NULL); + dom_node_mark_pending(a); + + return DOM_NO_ERR; +} + +/** + * Test whether certain attribute is inside the hash table + * + * \param element The element + * \param hs The hash table contains the attributes + * \param name The attribute's name + * \param result The return value + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_element_has_attr(struct dom_element *element, + struct dom_hash_table *hs, struct dom_string *name, bool *result) { - *result = (element->attributes != NULL); + void *a; + dom_exception err; + lwc_string *str; + + /* Looking for name */ + err = _dom_node_get_intern_string(&element->base, name, &str); + if (err != DOM_NO_ERR) + return err; + + a = _dom_hash_get(hs, str); + + /* Fill in value */ + if (a == NULL) { + *result = false; + } else { + *result = true; + } return DOM_NO_ERR; } /** - * Retrieve a pointer to the first attribute attached to an element + * (Un)set an attribute Node as a ID. * - * \param element The element to retrieve the first attribute from - * \return Pointer to first attribute, or NULL if none. + * \param element The element contains the attribute + * \param hs The hash table which contains the attribute node + * \param name The name of the attribute + * \param is_id true for set the node as a ID attribute, false unset it + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. */ -struct dom_node_internal *dom_element_get_first_attribute( - struct dom_element *element) +dom_exception _dom_element_set_id_attr(struct dom_element *element, + struct dom_hash_table *hs, struct dom_string *name, bool is_id) +{ + dom_attr *attr; + lwc_string *str; + dom_exception err; + struct dom_hash_table *oh; + + /* Looking for name */ + err = _dom_node_get_intern_string(&element->base, name, &str); + if (err != DOM_NO_ERR) + return err; + + attr = (dom_attr *) _dom_hash_get(hs, str); + if (attr == NULL) + return DOM_NOT_FOUND_ERR; + + if (is_id == true) { + /* Firstly, clear the previous id attribute if there is one */ + if (element->id_ns != NULL) { + assert(element->id_name != NULL); + oh = (struct dom_hash_table *) _dom_hash_get( + element->ns_attributes, element->id_ns); + } else { + oh = element->attributes; + } + assert(oh != NULL); + + if (element->id_name != NULL) { + attr = (dom_attr *) _dom_hash_get(oh, element->id_name); + assert(attr != NULL); + _dom_attr_set_isid(attr, false); + } + } + + _dom_attr_set_isid(attr, is_id); + + element->id_name = str; + + return DOM_NO_ERR; +} + +/** + * Get the ID string of the element + * + * \param ele The element + * \param id The ID of this element + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_element_get_id(struct dom_element *ele, lwc_string **id) { - return (struct dom_node_internal *) element->attributes; + dom_exception err; + dom_string *ret = NULL; + + *id = NULL; + + if (ele->id_ns != NULL && ele->id_name != NULL) { + /* There is user specific ID attribute */ + dom_document *doc; + doc = dom_node_get_owner(ele); + assert(doc != NULL); + + dom_string *namespace, *name; + err = _dom_document_create_string_from_lwcstring(doc, + ele->id_ns, &namespace); + if (err != DOM_NO_ERR) + return err; + + err = _dom_document_create_string_from_lwcstring(doc, + ele->id_name, &name); + if (err != DOM_NO_ERR) { + dom_string_unref(namespace); + return err; + } + + err = _dom_element_get_attribute_ns(ele, namespace, name, &ret); + if (err != DOM_NO_ERR) { + dom_string_unref(namespace); + dom_string_unref(name); + return err; + } + + dom_string_unref(namespace); + dom_string_unref(name); + + err = _dom_node_get_intern_string((dom_node_internal *) ele, + ret, id); + dom_string_unref(ret); + return err; + } + + dom_document *doc; + doc = dom_node_get_owner(ele); + assert(doc != NULL); + dom_string *name; + + if (ele->id_name != NULL) { + err = _dom_document_create_string_from_lwcstring(doc, + ele->id_name, &name); + if (err != DOM_NO_ERR) { + return err; + } + } else { + lwc_string *id_name = _dom_document_get_id_name(doc); + if (id_name == NULL) { + /* No ID attribute at all, just return NULL */ + *id = NULL; + return DOM_NO_ERR; + } + err = _dom_document_create_string_from_lwcstring(doc, id_name, + &name); + if (err != DOM_NO_ERR) { + return err; + } + } + + err = _dom_element_get_attribute(ele, name, &ret); + if (err != DOM_NO_ERR) { + dom_string_unref(name); + return err; + } + + dom_string_unref(name); + + if (ret != NULL) { + err = _dom_node_get_intern_string((dom_node_internal *) ele, + ret, id); + dom_string_unref(ret); + } else { + *id = NULL; + } + + return err; +} + + +/* The hash function for attributes and id tables */ +unsigned int _dom_element_hash_lwcstring(void *key) +{ + lwc_string *lstr = (lwc_string *) key; + + return lwc_string_hash_value(lstr); +} + +/*-------------- The dom_namednodemap functions -------------------------*/ + +/* Implementation function for NamedNodeMap, see core/namednodemap.h for + * details */ +dom_exception attributes_get_length(void *priv, + unsigned long *length) +{ + unsigned int ret = 0; + unsigned int c1, *c2 = NULL; + void *key, *value; + dom_element *e = (dom_element *) priv; + + ret += _dom_hash_get_length(e->attributes); + while( (key = _dom_hash_iterate(e->ns_attributes, &c1, &c2)) != NULL) { + value = _dom_hash_get(e->ns_attributes, key); + if (value != NULL) { + ret += _dom_hash_get_length( + (struct dom_hash_table *) value); + } + } + + *length = ret; + return DOM_NO_ERR; +} + +/* Implementation function for NamedNodeMap, see core/namednodemap.h for + * details */ +dom_exception attributes_get_named_item(void *priv, + struct dom_string *name, struct dom_node **node) +{ + dom_element *e = (dom_element *) priv; + + return _dom_element_get_attribute_node(e, name, (dom_attr **) node); +} + +/* Implementation function for NamedNodeMap, see core/namednodemap.h for + * details */ +dom_exception attributes_set_named_item(void *priv, + struct dom_node *arg, struct dom_node **node) +{ + dom_element *e = (dom_element *) priv; + dom_node_internal *n = (dom_node_internal *) arg; + + if (n->type != DOM_ATTRIBUTE_NODE) + return DOM_HIERARCHY_REQUEST_ERR; + + return _dom_element_set_attribute_node(e, (dom_attr *) arg, + (dom_attr **) node); +} + +/* Implementation function for NamedNodeMap, see core/namednodemap.h for + * details */ +dom_exception attributes_remove_named_item( + void *priv, struct dom_string *name, + struct dom_node **node) +{ + dom_element *e = (dom_element *) priv; + dom_exception err; + + err = _dom_element_get_attribute_node(e, name, (dom_attr **) node); + if (err != DOM_NO_ERR) + return err; + + if (*node == NULL) { + return DOM_NOT_FOUND_ERR; + } + + return _dom_element_remove_attribute(e, name); +} + +/* Implementation function for NamedNodeMap, see core/namednodemap.h for + * details */ +dom_exception attributes_item(void *priv, + unsigned long index, struct dom_node **node) +{ + struct dom_hash_table *ht = NULL; + unsigned int num = index + 1; + unsigned int len; + dom_element *e = (dom_element *) priv; + void *key, *value; + unsigned int c1, *c2 = NULL; + + len = _dom_hash_get_length(e->attributes); + if (num <= len) { + ht = e->attributes; + } else { + num -= len; + } + + while( (key = _dom_hash_iterate(e->ns_attributes, &c1, &c2)) != NULL) { + value = _dom_hash_get(e->ns_attributes, key); + if (value != NULL) { + len = _dom_hash_get_length( + (struct dom_hash_table *) value); + if (num <= len) { + ht = (struct dom_hash_table *) value; + break; + } else { + num -= len; + } + } + } + + *node = NULL; + c2 = NULL; + if (ht != NULL) + { + while( (key = _dom_hash_iterate(ht, &c1, &c2)) != NULL) { + value = _dom_hash_get(ht, key); + if (--num == 0) { + *node = (dom_node *) value; + break; + } + } + } + + if (*node != NULL) + dom_node_ref(*node); + + return DOM_NO_ERR; +} + +/* Implementation function for NamedNodeMap, see core/namednodemap.h for + * details */ +dom_exception attributes_get_named_item_ns( + void *priv, struct dom_string *namespace, + struct dom_string *localname, struct dom_node **node) +{ + dom_element *e = (dom_element *) priv; + + return _dom_element_get_attribute_node_ns(e, namespace, localname, + (dom_attr **) node); +} + +/* Implementation function for NamedNodeMap, see core/namednodemap.h for + * details */ +dom_exception attributes_set_named_item_ns( + void *priv, struct dom_node *arg, + struct dom_node **node) +{ + dom_element *e = (dom_element *) priv; + dom_node_internal *n = (dom_node_internal *) arg; + + if (n->type != DOM_ATTRIBUTE_NODE) + return DOM_HIERARCHY_REQUEST_ERR; + + return _dom_element_set_attribute_node_ns(e, (dom_attr *) arg, + (dom_attr **) node); +} + +/* Implementation function for NamedNodeMap, see core/namednodemap.h for + * details */ +dom_exception attributes_remove_named_item_ns( + void *priv, struct dom_string *namespace, + struct dom_string *localname, struct dom_node **node) +{ + dom_element *e = (dom_element *) priv; + dom_exception err; + + err = _dom_element_get_attribute_node_ns(e, namespace, localname, + (dom_attr **) node); + if (err != DOM_NO_ERR) + return err; + + if (*node == NULL) { + return DOM_NOT_FOUND_ERR; + } + + return _dom_element_remove_attribute_ns(e, namespace, localname); +} + +/* Implementation function for NamedNodeMap, see core/namednodemap.h for + * details */ +void attributes_destroy(void *priv) +{ + dom_element *e = (dom_element *) priv; + + dom_node_unref(e); +} + +/* Implementation function for NamedNodeMap, see core/namednodemap.h for + * details */ +bool attributes_equal(void *p1, void *p2) +{ + /* We have passed the pointer to this element as the private data, + * and here we just need to compare whether the two elements are + * equal + */ + return p1 == p2; +} +/*------------------ End of namednodemap functions -----------------------*/ + +/* The key_func of the hash table, see utils/hashtable.h for details */ +void *_key(void *key, void *key_pw, dom_alloc alloc, void *pw, + bool clone) +{ + assert(key != NULL); + assert(key_pw != NULL); + + UNUSED(alloc); + UNUSED(pw); + + if (clone == false) { + lwc_context_string_unref((lwc_context *) key_pw, + (lwc_string *) key); + return NULL; + } else { + lwc_error err; + lwc_string *ret; + const char *data = lwc_string_data((lwc_string *) key); + size_t len = lwc_string_length((lwc_string *) key); + err = lwc_context_intern((lwc_context *) key_pw, data, len, + &ret); + if (err != lwc_error_ok) + return NULL; + + return ret; + } +} + +/* The value_func of the hash table, see utils/hashtable.h for details */ +void *_value(void *value, void *value_pw, dom_alloc alloc, + void *pw, bool clone) +{ + assert(value != NULL); + assert(value_pw != NULL); + + UNUSED(alloc); + UNUSED(pw); + UNUSED(value_pw); + + if (clone == false) { + dom_node_internal *a = (dom_node_internal *) value; + a->parent = NULL; + dom_node_try_destroy(a); + return NULL; + } else { + dom_exception err; + dom_node *node; + + err = dom_document_import_node((dom_document *) value_pw, value, + true, &node); + if (err != DOM_NO_ERR) + return NULL; + + return node; + } +} + +/* The value_func of the hash table, see utils/hashtable.h for details */ +void *_nsattributes(void *value, void *value_pw, dom_alloc alloc, + void *pw, bool clone) +{ + assert(value != NULL); + assert(value_pw != NULL); + + UNUSED(alloc); + UNUSED(pw); + + if (clone == false) { + _dom_hash_destroy((struct dom_hash_table *) value, _key, + value_pw, _value, value_pw); + return NULL; + } else { + dom_document *doc = (dom_document *) value_pw; + lwc_context *ctx = _dom_document_get_intern_context(doc); + assert(ctx != NULL); + dom_alloc alloc; + void *pw; + struct dom_hash_table *ret = NULL; + _dom_document_get_allocator(doc, &alloc, &pw); + + ret = _dom_hash_clone((struct dom_hash_table *) value, alloc, + pw, _key, ctx, _value, doc); + + return ret; + } } |