From 399da01ae4eb5c5e3e9349bacc2063c946c3d4a1 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Tue, 11 Aug 2009 11:17:23 +0000 Subject: Merge the branches/struggleyb/libdom-remain back to trunk. svn path=/trunk/dom/; revision=9191 --- src/bootstrap/Makefile | 2 +- src/bootstrap/implementation.c | 423 +++++++++ src/bootstrap/implementation.h | 14 + src/bootstrap/implregistry.c | 71 +- src/bootstrap/init_fini.c | 19 +- src/core/Makefile | 13 +- src/core/attr.c | 342 +++++-- src/core/attr.h | 98 +- src/core/cdatasection.c | 66 +- src/core/cdatasection.h | 24 +- src/core/characterdata.c | 83 +- src/core/characterdata.h | 29 +- src/core/comment.c | 67 +- src/core/comment.h | 23 +- src/core/doc_fragment.c | 95 +- src/core/doc_fragment.h | 23 +- src/core/document.c | 1001 ++++++++++++++------ src/core/document.h | 108 ++- src/core/document_type.c | 224 ++++- src/core/document_type.h | 28 +- src/core/element.c | 2035 ++++++++++++++++++++++++++++------------ src/core/element.h | 108 ++- src/core/entity_ref.c | 98 +- src/core/entity_ref.h | 31 +- src/core/implementation.c | 25 +- src/core/impllist.c | 19 +- src/core/namednodemap.c | 361 ++----- src/core/namednodemap.h | 50 +- src/core/node.c | 1147 +++++++++++++++++----- src/core/node.h | 144 ++- src/core/nodelist.c | 214 +++-- src/core/nodelist.h | 26 +- src/core/pi.c | 65 +- src/core/pi.h | 22 +- src/core/string.c | 358 ++++++- src/core/string.h | 32 + src/core/text.c | 320 ++++++- src/core/text.h | 46 +- src/core/typeinfo.c | 80 ++ src/utils/Makefile | 3 +- src/utils/character_valid.c | 217 +++++ src/utils/character_valid.h | 54 ++ src/utils/hashtable.c | 492 ++++++++++ src/utils/hashtable.h | 42 + src/utils/list.h | 61 ++ src/utils/namespace.c | 71 +- src/utils/namespace.h | 7 + src/utils/resource_mgr.c | 105 +++ src/utils/resource_mgr.h | 45 + src/utils/validate.c | 177 ++++ src/utils/validate.h | 26 + 51 files changed, 7280 insertions(+), 1954 deletions(-) create mode 100644 src/bootstrap/implementation.c create mode 100644 src/bootstrap/implementation.h create mode 100644 src/core/string.h create mode 100644 src/core/typeinfo.c create mode 100644 src/utils/character_valid.c create mode 100644 src/utils/character_valid.h create mode 100644 src/utils/hashtable.c create mode 100644 src/utils/hashtable.h create mode 100644 src/utils/list.h create mode 100644 src/utils/resource_mgr.c create mode 100644 src/utils/resource_mgr.h create mode 100644 src/utils/validate.c create mode 100644 src/utils/validate.h (limited to 'src') diff --git a/src/bootstrap/Makefile b/src/bootstrap/Makefile index 820a4f4..e969d87 100644 --- a/src/bootstrap/Makefile +++ b/src/bootstrap/Makefile @@ -1,3 +1,3 @@ -DIR_SOURCES := implregistry.c init_fini.c +DIR_SOURCES := implregistry.c init_fini.c implementation.c include build/makefiles/Makefile.subdir diff --git a/src/bootstrap/implementation.c b/src/bootstrap/implementation.c new file mode 100644 index 0000000..d14fa5b --- /dev/null +++ b/src/bootstrap/implementation.c @@ -0,0 +1,423 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang + */ + +/** + * Note: The DOMImplementation Object here is a singleton object. It is + * initialised when the libDOM is initialised, it registers itself into + * the implreg and clients of it can get it by calling: + * + * dom_implregistry_get_dom_implementation or + * dom_implregistry_get_dom_implementation_list + * + */ + +#include +#include +#include + +#include + +#include "core/node.h" +#include "core/document_type.h" + +#include "utils/utils.h" +#include "utils/validate.h" +#include "utils/namespace.h" + +#include "bootstrap/implementation.h" + +static dom_alloc _alloc; +static void *_pw; + +static dom_exception impl_get_dom_implementation( + struct dom_string *features, + struct dom_implementation **impl); +static dom_exception impl_get_dom_implementation_list( + struct dom_string *features, + struct dom_implementation_list **list); + +static dom_exception impl_implementation_has_feature( + struct dom_implementation *impl, + struct dom_string *feature, + struct dom_string *version, + bool *result); +static dom_exception impl_implementation_create_document_type( + struct dom_implementation *impl, + struct dom_string *qname, + struct dom_string *public_id, + struct dom_string *system_id, + dom_alloc alloc, void *pw, struct lwc_context_s *ctx, + struct dom_document_type **doctype); +static dom_exception impl_implementation_create_document( + struct dom_implementation *impl, + struct dom_string *namespace, + struct dom_string *qname, + struct dom_document_type *doctype, + dom_alloc alloc, void *pw, struct lwc_context_s *ctx, + struct dom_document **doc); +static dom_exception impl_implementation_get_feature( + struct dom_implementation *impl, + struct dom_string *feature, + struct dom_string *version, + void **object); +static void dom_implementation_destroy(struct dom_implementation *impl); + + +static struct dom_implementation_source dom_impl_src = { + impl_get_dom_implementation, + impl_get_dom_implementation_list +}; + +static struct dom_implementation dom_impl = { + impl_implementation_has_feature, + impl_implementation_create_document_type, + impl_implementation_create_document, + impl_implementation_get_feature, + dom_implementation_destroy, + 0 +}; + +/** + * Get a DOM implementation that supports the requested features + * + * \param features String containing required features + * \param impl Pointer to location to receive implementation + * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion + * + * Any memory allocated by this call should be allocated using + * the provided memory (de)allocation function. The implementation's + * destroy() method will be called once it is no longer used. + * + * The implementation will be referenced, so the client need not + * do this explicitly. The client must unref the implementation + * once it has finished with it. + */ +dom_exception impl_get_dom_implementation( + struct dom_string *features, + struct dom_implementation **impl) +{ + UNUSED(features); + + dom_impl.refcnt++; + + *impl = &dom_impl; + + return DOM_NO_ERR; +} + +/** + * Get a list of DOM implementations that support the requested + * features + * + * \param features String containing required features + * \param list Pointer to location to receive list + * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion + * + * Any memory allocated by this call should be allocated using + * the provided memory (de)allocation function. The ::alloc/::pw + * pair must be stored on the list object, such that the list + * and its contents may be freed once they are no longer needed. + * + * List nodes reference the implementation objects they point to. + * + * The list will be referenced, so the client need not do this + * explicitly. The client must unref the list once it has finished + * with it. + */ +dom_exception impl_get_dom_implementation_list( + struct dom_string *features, + struct dom_implementation_list **list) +{ + struct dom_implementation_list *l; + struct dom_implementation_list_item *i; + + UNUSED(features); + + l = _alloc(NULL, sizeof(struct dom_implementation_list), _pw); + if (l == NULL) + return DOM_NO_MEM_ERR; + + i = _alloc(NULL, sizeof(struct dom_implementation_list_item), _pw); + if (i == NULL) { + _alloc(l, 0, _pw); + return DOM_NO_MEM_ERR; + } + + i->impl = &dom_impl; + i->next = NULL; + i->prev = NULL; + + l->head = i; + + l->refcnt = 1; + + *list = l; + + return DOM_NO_ERR; +} + +/** + * Test whether a DOM implementation implements a specific feature + * and version + * + * \param impl The DOM implementation to query + * \param feature The feature to test for + * \param version The version number of the feature to test for + * \param result Pointer to location to receive result + * \return DOM_NO_ERR. + */ +dom_exception impl_implementation_has_feature( + struct dom_implementation *impl, + struct dom_string *feature, + struct dom_string *version, + bool *result) +{ + UNUSED(impl); + UNUSED(feature); + UNUSED(version); + UNUSED(result); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Create a document type node + * + * \param impl The implementation to create the node + * \param qname The qualified name of the document type + * \param public_id The external subset public identifier + * \param system_id The external subset system identifier + * \param doctype Pointer to location to receive result + * \return DOM_NO_ERR on success, + * DOM_INVALID_CHARACTER_ERR if ::qname is invalid, + * DOM_NAMESPACE_ERR if ::qname is malformed. + * + * Any memory allocated by this call should be allocated using + * the provided memory (de)allocation function. + * + * The doctype will be referenced, so the client need not do this + * explicitly. The client must unref the doctype once it has + * finished with it. + */ +dom_exception impl_implementation_create_document_type( + struct dom_implementation *impl, + struct dom_string *qname, + struct dom_string *public_id, + struct dom_string *system_id, + dom_alloc alloc, void *pw, struct lwc_context_s *ctx, + struct dom_document_type **doctype) +{ + struct dom_document_type *d; + struct dom_string *prefix = NULL, *lname = NULL; + dom_exception err; + + UNUSED(impl); + + if (qname != NULL && _dom_validate_name(qname) == false) + return DOM_INVALID_CHARACTER_ERR; + + err = _dom_namespace_split_qname(qname, &prefix, &lname); + if (err != DOM_NO_ERR) + return err; + + if ((prefix != NULL && _dom_validate_ncname(prefix) == false) || + (lname != NULL && _dom_validate_ncname(lname) == false)) + return DOM_NAMESPACE_ERR; + + /* Create the doctype */ + err = dom_document_type_create(qname, public_id, system_id, + alloc, pw, ctx, &d); + if (err != DOM_NO_ERR) + return err; + + *doctype = d; + if (prefix != NULL) + dom_string_unref(prefix); + if (lname != NULL) + dom_string_unref(lname); + + return DOM_NO_ERR; +} + +/** + * Create a document node + * + * \param impl The implementation to create the node + * \param namespace The namespace URI of the document element + * \param qname The qualified name of the document element + * \param doctype The type of document to create + * \param doc Pointer to location to receive result + * \return DOM_NO_ERR on success, + * DOM_INVALID_CHARACTER_ERR if ::qname is invalid, + * DOM_NAMESPACE_ERR if ::qname is malformed, or if + * ::qname has a prefix and + * ::namespace is NULL, or if + * ::qname is NULL and ::namespace + * is non-NULL, or if ::qname has + * a prefix "xml" and ::namespace + * is not + * "http://www.w3.org/XML/1998/namespace", + * or if ::impl does not support + * the "XML" feature and + * ::namespace is non-NULL, + * DOM_WRONG_DOCUMENT_ERR if ::doctype is already being + * used by a document, or if it + * was not created by ::impl, + * DOM_NOT_SUPPORTED_ERR if ::impl does not support the + * feature "XML" and the language + * exposed through Document does + * not support XML namespaces. + * + * Any memory allocated by this call should be allocated using + * the provided memory (de)allocation function. + * + * The document will be referenced, so the client need not do this + * explicitly. The client must unref the document once it has + * finished with it. + */ +dom_exception impl_implementation_create_document( + struct dom_implementation *impl, + struct dom_string *namespace, + struct dom_string *qname, + struct dom_document_type *doctype, + dom_alloc alloc, void *pw, struct lwc_context_s *ctx, + struct dom_document **doc) +{ + struct dom_document *d; + dom_exception err; + + if (qname != NULL && _dom_validate_name(qname) == false) + return DOM_INVALID_CHARACTER_ERR; + + err = _dom_namespace_validate_qname(qname, namespace); + if (err != DOM_NO_ERR) + return DOM_NAMESPACE_ERR; + + if (doctype != NULL) { + if (dom_node_get_parent(doctype) != NULL || + _dom_document_type_get_impl(doctype) != + impl) + return DOM_WRONG_DOCUMENT_ERR; + } + + /* Create document object */ + err = dom_document_create(impl, alloc, pw, ctx, &d); + if (err != DOM_NO_ERR) + return err; + + /* Set its doctype, if necessary */ + if (doctype != NULL) { + struct dom_node *ins_doctype = NULL; + + err = dom_node_append_child((struct dom_node *) d, + (struct dom_node *) doctype, &ins_doctype); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) d); + return err; + } + + /* Not interested in inserted doctype */ + if (ins_doctype != NULL) + dom_node_unref(ins_doctype); + } + + /* Create root element and attach it to document */ + if (qname != NULL) { + struct dom_element *e; + struct dom_node *inserted; + + err = dom_document_create_element_ns(d, namespace, qname, &e); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) d); + return err; + } + + err = dom_node_append_child((struct dom_node *) d, + (struct dom_node *) e, &inserted); + if (err != DOM_NO_ERR) { + dom_node_unref((struct dom_node *) e); + dom_node_unref((struct dom_node *) d); + return err; + } + + /* No longer interested in inserted node */ + dom_node_unref(inserted); + + /* Done with element */ + dom_node_unref((struct dom_node *) e); + } + + *doc = d; + + return DOM_NO_ERR; +} + +/** + * Retrieve a specialized object which implements the specified + * feature and version + * + * \param impl The implementation to create the object + * \param feature The requested feature + * \param version The version number of the feature + * \param object Pointer to location to receive object + * \return DOM_NO_ERR. + * + * Any memory allocated by this call should be allocated using + * the provided memory (de)allocation function. + */ +dom_exception impl_implementation_get_feature( + struct dom_implementation *impl, + struct dom_string *feature, + struct dom_string *version, + void **object) +{ + UNUSED(impl); + UNUSED(feature); + UNUSED(version); + UNUSED(object); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Destroy a DOM implementation instance + * + * \param impl The instance to destroy + */ +void dom_implementation_destroy(struct dom_implementation *impl) +{ + UNUSED(impl); + + /* Nothing to do -- we're statically allocated */ +} + +/** + * Initialise the DOM implementation + * + * \param alloc Pointer to memory (de)allocation function + * \param pw Pointer to client-specific private data + * \return DOM_NO_ERR on success + */ +dom_exception _dom_implementation_initialise(dom_alloc alloc, void *pw) +{ + _alloc = alloc; + _pw = pw; + + return dom_register_source(&dom_impl_src); +} + +/** + * Finalise the DOM implementation + */ +void _dom_implementation_finalise(void) +{ + _alloc = NULL; + _pw = NULL; +} + + diff --git a/src/bootstrap/implementation.h b/src/bootstrap/implementation.h new file mode 100644 index 0000000..f62077c --- /dev/null +++ b/src/bootstrap/implementation.h @@ -0,0 +1,14 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2009 Bo Yang + */ + +#ifndef dom_bootstrap_implementation_h_ +#define dom_bootstrap_implementation_h_ + +dom_exception _dom_implementation_initialise(dom_alloc alloc, void *pw); +void _dom_implementation_finalise(void); + +#endif diff --git a/src/bootstrap/implregistry.c b/src/bootstrap/implregistry.c index 9407ee1..91e4068 100644 --- a/src/bootstrap/implregistry.c +++ b/src/bootstrap/implregistry.c @@ -11,6 +11,9 @@ #include #include +#include + +void dom_implementation_list_destroy(struct dom_implementation_list *list); /** * Item in list of registered DOM implementation sources @@ -23,14 +26,30 @@ struct dom_impl_src_item { }; static struct dom_impl_src_item *sources; /**< List of registered sources */ +static dom_alloc alloc; +static void *pw; + +/** + * Initialise the implementation registry + * + * \param allocator The memory allocator + * \param ptr Private data pointer of allocator + * \return DOM_NO_ERR on success + */ +dom_exception dom_implregistry_initialise( + dom_alloc allocator, void *ptr) +{ + alloc = allocator; + pw = ptr; + + return DOM_NO_ERR; +} /** * Retrieve a DOM implementation from the registry * * \param features String containing required features * \param impl Pointer to location to receive implementation - * \param alloc Function to (de)allocate memory - * \param pw Pointer to client-specific private data * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion * * Any memory allocated by this call should be allocated using @@ -43,16 +62,14 @@ static struct dom_impl_src_item *sources; /**< List of registered sources */ */ dom_exception dom_implregistry_get_dom_implementation( struct dom_string *features, - struct dom_implementation **impl, - dom_alloc alloc, void *pw) + struct dom_implementation **impl) { struct dom_impl_src_item *item; struct dom_implementation *found = NULL; dom_exception err; for (item = sources; item; item = item->next) { - err = item->source->get_dom_implementation(features, &found, - alloc, pw); + err = item->source->get_dom_implementation(features, &found); if (err != DOM_NO_ERR) return err; @@ -71,14 +88,10 @@ dom_exception dom_implregistry_get_dom_implementation( * * \param features String containing required features * \param list Pointer to location to receive list - * \param alloc Function to (de)allocate memory - * \param pw Pointer to client-specific private data * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion * * Any memory allocated by this call should be allocated using - * the provided memory (de)allocation function. The ::alloc/::pw - * pair must be stored on the list object, such that the list - * and its contents may be freed once they are no longer needed. + * the provided memory (de)allocation function. * * List nodes reference the implementation objects they point to. * @@ -88,8 +101,7 @@ dom_exception dom_implregistry_get_dom_implementation( */ dom_exception dom_implregistry_get_dom_implementation_list( struct dom_string *features, - struct dom_implementation_list **list, - dom_alloc alloc, void *pw) + struct dom_implementation_list **list) { struct dom_implementation_list *l; struct dom_impl_src_item *item; @@ -100,16 +112,15 @@ dom_exception dom_implregistry_get_dom_implementation_list( return DOM_NO_MEM_ERR; l->head = NULL; - l->alloc = alloc; - l->pw = pw; l->refcnt = 1; + l->destroy = dom_implementation_list_destroy; for (item = sources; item; item = item->next) { struct dom_implementation_list *plist = NULL; struct dom_implementation_list_item *plast = NULL; err = item->source->get_dom_implementation_list(features, - &plist, alloc, pw); + &plist); if (err != DOM_NO_ERR) { dom_implementation_list_unref(l); return err; @@ -156,12 +167,9 @@ dom_exception dom_implregistry_get_dom_implementation_list( * Register a DOM implementation source with the DOM library * * \param source The implementation source to register - * \param alloc Memory (de)allocation function - * \param pw Pointer to client-specific private data * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion. */ -dom_exception dom_register_source(struct dom_implementation_source *source, - dom_alloc alloc, void *pw) +dom_exception dom_register_source(struct dom_implementation_source *source) { struct dom_impl_src_item *item; @@ -182,3 +190,26 @@ dom_exception dom_register_source(struct dom_implementation_source *source, return DOM_NO_ERR; } +/** + * Destroy a dom_implementation_list + * + * \param list The list to destory + */ +void dom_implementation_list_destroy(struct dom_implementation_list *list) +{ + struct dom_implementation_list_item *i, *j; + + /* Destroy all list entries */ + for (i = list->head; i; i = j) { + j = i->next; + + /* Unreference the implementation */ + dom_implementation_unref(i->impl); + + /* And free the entry */ + alloc(i, 0, pw); + } + + /* Free the list object */ + alloc(list, 0, pw); +} diff --git a/src/bootstrap/init_fini.c b/src/bootstrap/init_fini.c index a5a62a1..f3c7290 100644 --- a/src/bootstrap/init_fini.c +++ b/src/bootstrap/init_fini.c @@ -8,9 +8,10 @@ #include #include +#include -#include "core/document.h" #include "utils/namespace.h" +#include "bootstrap/implementation.h" static bool __initialised; @@ -32,16 +33,21 @@ dom_exception dom_initialise(dom_alloc alloc, void *pw) return DOM_NO_ERR; } - err = _dom_document_initialise(alloc, pw); + err = _dom_namespace_initialise(alloc, pw); if (err != DOM_NO_ERR) { return err; } - err = _dom_namespace_initialise(alloc, pw); + err = dom_implregistry_initialise(alloc, pw); if (err != DOM_NO_ERR) { return err; } + err = _dom_implementation_initialise(alloc, pw); + if (err != DOM_NO_ERR) { + return err; + } + __initialised = true; return DOM_NO_ERR; @@ -63,12 +69,9 @@ dom_exception dom_finalise(void) return DOM_NO_ERR; } - err = _dom_namespace_finalise(); - if (err != DOM_NO_ERR) { - return err; - } + _dom_implementation_finalise(); - err = _dom_document_finalise(); + err = _dom_namespace_finalise(); if (err != DOM_NO_ERR) { return err; } diff --git a/src/core/Makefile b/src/core/Makefile index 9abadad..46cb48d 100644 --- a/src/core/Makefile +++ b/src/core/Makefile @@ -1,8 +1,11 @@ # Sources -DIR_SOURCES := attr.c cdatasection.c characterdata.c comment.c \ - document.c document_type.c doc_fragment.c \ - element.c entity_ref.c implementation.c impllist.c \ - namednodemap.c node.c nodelist.c \ - pi.c string.c text.c +DIR_SOURCES := \ + string.c node.c \ + attr.c characterdata.c element.c \ + implementation.c impllist.c \ + text.c typeinfo.c comment.c \ + namednodemap.c nodelist.c \ + cdatasection.c document_type.c entity_ref.c pi.c \ + doc_fragment.c document.c include build/makefiles/Makefile.subdir diff --git a/src/core/attr.c b/src/core/attr.c index 9fbb06f..df95482 100644 --- a/src/core/attr.c +++ b/src/core/attr.c @@ -3,10 +3,12 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ #include #include +#include #include #include @@ -25,24 +27,34 @@ struct dom_element; * DOM node attribute */ struct dom_attr { - struct dom_node_internal base; /**< Base node */ + struct dom_node_internal base; /**< Base node */ - bool specified; /**< Whether attribute was specified - * or defaulted */ + bool specified; /**< Whether the attribute is specified + * or default */ struct dom_type_info *schema_type_info; /**< Type information */ - bool is_id; /**< Attribute is of type ID */ + bool is_id; /**< Whether this attribute is a ID attribute */ }; -/* The vtable for dom attr node */ +/* The vtable for dom_attr node */ static struct dom_attr_vtable attr_vtable = { { - DOM_NODE_VTABLE + DOM_NODE_VTABLE_ATTR }, DOM_ATTR_VTABLE }; +/* The protected vtable for dom_attr */ +static struct dom_node_protect_vtable attr_protect_vtable = { + DOM_ATTR_PROTECT_VTABLE +}; + + +/* -------------------------------------------------------------------- */ + +/* Constructor and destructor */ + /** * Create an attribute node * @@ -50,43 +62,69 @@ static struct dom_attr_vtable attr_vtable = { * \param name The (local) name of the node to create * \param namespace The namespace URI of the attribute, or NULL * \param prefix The namespace prefix of the attribute, or NULL + * \param specified Whtether this attribute is specified * \param result Pointer to location to receive created attribute * \return DOM_NO_ERR on success, - * DOM_INVALID_CHARACTER_ERR if ::name is invalid, * DOM_NO_MEM_ERR on memory exhaustion. * - * ::doc and ::name will have their reference counts increased. + * ::doc and ::name will have their reference counts increased. The + * caller should make sure that ::name is a valid NCName here. * * The returned attribute will already be referenced. */ -dom_exception dom_attr_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *namespace, - struct dom_string *prefix, struct dom_attr **result) +dom_exception _dom_attr_create(struct dom_document *doc, + struct lwc_string_s *name, struct lwc_string_s *namespace, + struct lwc_string_s *prefix, bool specified, + struct dom_attr **result) { struct dom_attr *a; dom_exception err; - /** \todo Sanity check the attribute name */ - - /* Allocate the element */ - a = dom_document_alloc(doc, NULL, sizeof(struct dom_attr)); + /* Allocate the attribute node */ + a = _dom_document_alloc(doc, NULL, sizeof(struct dom_attr)); if (a == NULL) return DOM_NO_MEM_ERR; /* Initialise the vtable */ a->base.base.vtable = &attr_vtable; - a->base.destroy = _dom_attr_destroy; + a->base.vtable = &attr_protect_vtable; + + /* Initialise the class */ + err = _dom_attr_initialise(a, doc, name, namespace, prefix, specified, + result); + if (err != DOM_NO_ERR) { + _dom_document_alloc(doc, a, 0); + return err; + } + return DOM_NO_ERR; +} + +/** + * Initialise a dom_attr + * + * \param a The dom_attr + * \param doc The document + * \param name The name of this attribute node + * \param namespace The namespace of this attribute + * \param prefix The prefix + * \param specified Whether this node is a specified one + * \param result The returned node + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_attr_initialise(dom_attr *a, + struct dom_document *doc, struct lwc_string_s *name, + struct lwc_string_s *namespace, struct lwc_string_s *prefix, + bool specified, struct dom_attr **result) +{ + dom_exception err; - /* Initialise the base class */ - err = dom_node_initialise(&a->base, doc, DOM_ATTRIBUTE_NODE, + err = _dom_node_initialise(&a->base, doc, DOM_ATTRIBUTE_NODE, name, NULL, namespace, prefix); if (err != DOM_NO_ERR) { - dom_document_alloc(doc, a, 0); return err; } - /* Perform our type-specific initialisation */ - a->specified = false; + a->specified = specified; a->schema_type_info = NULL; a->is_id = false; @@ -96,61 +134,48 @@ dom_exception dom_attr_create(struct dom_document *doc, } /** - * Destroy an attribute node + * The destructor of dom_attr * - * \param doc The owning document - * \param attr The attribute to destroy - * - * The contents of ::attr will be destroyed and ::attr will be freed + * \param doc The owner document + * \param attr The attribute */ -void dom_attr_destroy(struct dom_document *doc, struct dom_attr *attr) +void _dom_attr_finalise(dom_document *doc, dom_attr *attr) { - struct dom_node_internal *c, *d; - - /* Destroy children of this node */ - for (c = attr->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); - } - /* Now, clean up this node and destroy it */ if (attr->schema_type_info != NULL) { /** \todo destroy schema type info */ } - dom_node_finalise(doc, &attr->base); - - dom_document_alloc(doc, attr, 0); + _dom_node_finalise(doc, &attr->base); } -void _dom_attr_destroy(dom_node_internal *node) +/** + * Destroy an attribute node + * + * \param doc The owning document + * \param attr The attribute to destroy + * + * The contents of ::attr will be destroyed and ::attr will be freed + */ +void _dom_attr_destroy(struct dom_document *doc, struct dom_attr *attr) { - UNUSED(node); + _dom_attr_finalise(doc, attr); + + _dom_document_alloc(doc, attr, 0); } + +/* -------------------------------------------------------------------- */ + +/* The public virtual functions */ + /** * Retrieve an attribute's name * * \param attr Attribute to retrieve name from * \param result Pointer to location to receive result - * \return DOM_NO_ERR. + * \return DOM_NO_ERR on success, appropriate dom_exception on failure * * The returned string will have its reference count increased. It is * the responsibility of the caller to unref the string once it has @@ -164,7 +189,7 @@ dom_exception _dom_attr_get_name(struct dom_attr *attr, } /** - * Determine if attribute was specified or defaulted + * Determine if attribute was specified or default * * \param attr Attribute to inspect * \param result Pointer to location to receive result @@ -182,7 +207,7 @@ dom_exception _dom_attr_get_specified(struct dom_attr *attr, bool *result) * * \param attr Attribute to retrieve value from * \param result Pointer to location to receive result - * \return DOM_NO_ERR. + * \return DOM_NO_ERR on success, appropriate dom_exception on failure * * The returned string will have its reference count increased. It is * the responsibility of the caller to unref the string once it has @@ -196,8 +221,8 @@ dom_exception _dom_attr_get_value(struct dom_attr *attr, struct dom_string *value, *temp; dom_exception err; - err = dom_document_create_string(a->owner, - (const uint8_t *) "", SLEN(""), &value); + err = _dom_document_create_string(a->owner, + NULL, 0, &value); if (err != DOM_NO_ERR) { return err; } @@ -221,7 +246,7 @@ dom_exception _dom_attr_get_value(struct dom_attr *attr, struct dom_string *tr; /* Get textual representation of entity */ - err = dom_entity_reference_get_textual_representation( + err = _dom_entity_reference_get_textual_representation( (struct dom_entity_reference *) c, &tr); if (err != DOM_NO_ERR) { @@ -285,24 +310,21 @@ dom_exception _dom_attr_set_value(struct dom_attr *attr, /* 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); + dom_node_try_destroy(c); } /* And insert the text node as the value */ ((struct dom_node_internal *) text)->parent = a; a->first_child = a->last_child = (struct dom_node_internal *) text; + dom_node_unref(text); + dom_node_remove_pending(text); + + /* Now the attribute node is specified */ + attr->specified = true; return DOM_NO_ERR; } @@ -336,7 +358,7 @@ dom_exception _dom_attr_get_owner(struct dom_attr *attr, * * \param attr The attribute to extract type information from * \param result Pointer to location to receive result - * \return DOM_NO_ERR. + * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. * * The returned type info will have its reference count increased. The caller * should unref it once it has finished with it. @@ -363,3 +385,179 @@ dom_exception _dom_attr_is_id(struct dom_attr *attr, bool *result) return DOM_NO_ERR; } + +/*------------- The overload virtual functions ------------------------*/ + +/* Overload function of Node, please refer node.c for the detail of this + * function. */ +dom_exception _dom_attr_get_node_value(dom_node_internal *node, + struct dom_string **result) +{ + dom_attr *attr = (dom_attr *) node; + + return _dom_attr_get_value(attr, result); +} + +/* Overload function of Node, please refer node.c for the detail of this + * function. */ +dom_exception _dom_attr_clone_node(dom_node_internal *node, bool deep, + dom_node_internal **result) +{ + dom_exception err; + dom_attr *attr; + + /* Discard the warnings */ + UNUSED(deep); + + /* Clone an Attr always clone all its children */ + err = _dom_node_clone_node(node, true, result); + if (err != DOM_NO_ERR) + return err; + + attr = (dom_attr *) *result; + /* Clone an Attr always result a specified Attr, + * see DOM Level 3 Node.cloneNode */ + attr->specified = true; + + return DOM_NO_ERR; +} + +/* Overload function of Node, please refer node.c for the detail of this + * function. */ +dom_exception _dom_attr_set_prefix(dom_node_internal *node, + struct dom_string *prefix) +{ + /* Really I don't know whether there should something + * special to do here */ + return _dom_node_set_prefix(node, prefix); +} + +/* Overload function of Node, please refer node.c for the detail of this + * function. */ +dom_exception _dom_attr_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); +} + +/* Overload function of Node, please refer node.c for the detail of this + * function. */ +dom_exception _dom_attr_is_default_namespace(dom_node_internal *node, + struct dom_string *namespace, bool *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 = false; + return DOM_NO_ERR; + } + + return dom_node_is_default_namespace(owner, namespace, result); +} + +/* Overload function of Node, please refer node.c for the detail of this + * function. */ +dom_exception _dom_attr_lookup_namespace(dom_node_internal *node, + struct dom_string *prefix, 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_namespace(owner, prefix, result); +} + + +/*----------------------------------------------------------------------*/ + +/* The protected virtual functions */ + +/* The virtual destroy function of this class */ +void __dom_attr_destroy(dom_node_internal *node) +{ + dom_document *doc = node->owner; + + assert(doc != NULL); + _dom_attr_destroy(doc, (dom_attr *) node); +} + +/* The memory allocator of this class */ +dom_exception _dom_attr_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + UNUSED(n); + dom_attr *a; + + a = _dom_document_alloc(doc, NULL, sizeof(struct dom_attr)); + if (a == NULL) + return DOM_NO_MEM_ERR; + + *ret = (dom_node_internal *) a; + dom_node_set_owner(*ret, doc); + + return DOM_NO_ERR; +} + +/* The copy constructor of this class */ +dom_exception _dom_attr_copy(struct dom_node_internal *new, + struct dom_node_internal *old) +{ + dom_attr *na = (dom_attr *) new; + dom_attr *oa = (dom_attr *) old; + + na->specified = oa->specified; + na->is_id = oa->is_id; + + /* TODO: deal with dom_type_info, it get no definition ! */ + + return _dom_node_copy(new, old); +} + + +/** + * Set/Unset whether this attribute is a ID attribute + * + * \param attr The attribute + * \param is_id Whether it is a ID attribute + */ +void _dom_attr_set_isid(struct dom_attr *attr, bool is_id) +{ + attr->is_id = is_id; +} + +/** + * Set/Unset whether the attribute is a specified one. + * + * \param attr The attribute node + * \param specified Whether this attribute is a specified one + */ +void _dom_attr_set_specified(struct dom_attr *attr, bool specified) +{ + attr->specified = specified; +} + diff --git a/src/core/attr.h b/src/core/attr.h index 2a3cd67..84b741f 100644 --- a/src/core/attr.h +++ b/src/core/attr.h @@ -13,26 +13,31 @@ struct dom_document; struct dom_string; struct dom_type_info; +struct lwc_string_s; -dom_exception dom_attr_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *namespace, - struct dom_string *prefix, struct dom_attr **result); -void dom_attr_destroy(struct dom_document *doc, struct dom_attr *attr); -/* The virtual destroy function */ -void _dom_attr_destroy(dom_node_internal *node); +dom_exception _dom_attr_create(struct dom_document *doc, + struct lwc_string_s *name, struct lwc_string_s *namespace, + struct lwc_string_s *prefix, bool specified, + struct dom_attr **result); +void _dom_attr_destroy(struct dom_document *doc, struct dom_attr *attr); +dom_exception _dom_attr_initialise(struct dom_attr *a, + struct dom_document *doc, struct lwc_string_s *name, + struct lwc_string_s *namespace, struct lwc_string_s *prefix, + bool specified, struct dom_attr **result); +void _dom_attr_finalise(dom_document *doc, struct dom_attr *attr); -/* Virtual functions for dom attr */ +/* Virtual functions for dom_attr */ dom_exception _dom_attr_get_name(struct dom_attr *attr, - struct dom_string **result); + struct dom_string **result); dom_exception _dom_attr_get_specified(struct dom_attr *attr, bool *result); dom_exception _dom_attr_get_value(struct dom_attr *attr, - struct dom_string **result); + struct dom_string **result); dom_exception _dom_attr_set_value(struct dom_attr *attr, - struct dom_string *value); + struct dom_string *value); dom_exception _dom_attr_get_owner(struct dom_attr *attr, - struct dom_element **result); + struct dom_element **result); dom_exception _dom_attr_get_schema_type_info(struct dom_attr *attr, - struct dom_type_info **result); + struct dom_type_info **result); dom_exception _dom_attr_is_id(struct dom_attr *attr, bool *result); #define DOM_ATTR_VTABLE \ @@ -44,4 +49,73 @@ dom_exception _dom_attr_is_id(struct dom_attr *attr, bool *result); _dom_attr_get_schema_type_info, \ _dom_attr_is_id +/* Overloading dom_node functions */ +dom_exception _dom_attr_get_node_value(dom_node_internal *node, + struct dom_string **result); +dom_exception _dom_attr_clone_node(dom_node_internal *node, bool deep, + dom_node_internal **result); +dom_exception _dom_attr_set_prefix(dom_node_internal *node, + struct dom_string *prefix); +dom_exception _dom_attr_normalize(dom_node_internal *node); +dom_exception _dom_attr_lookup_prefix(dom_node_internal *node, + struct dom_string *namespace, struct dom_string **result); +dom_exception _dom_attr_is_default_namespace(dom_node_internal *node, + struct dom_string *namespace, bool *result); +dom_exception _dom_attr_lookup_namespace(dom_node_internal *node, + struct dom_string *prefix, struct dom_string **result); +#define DOM_NODE_VTABLE_ATTR \ + _dom_node_get_node_name, \ + _dom_attr_get_node_value, /*overload*/\ + _dom_node_set_node_value, \ + _dom_node_get_node_type, \ + _dom_node_get_parent_node, \ + _dom_node_get_child_nodes, \ + _dom_node_get_first_child, \ + _dom_node_get_last_child, \ + _dom_node_get_previous_sibling, \ + _dom_node_get_next_sibling, \ + _dom_node_get_attributes, \ + _dom_node_get_owner_document, \ + _dom_node_insert_before, \ + _dom_node_replace_child, \ + _dom_node_remove_child, \ + _dom_node_append_child, \ + _dom_node_has_child_nodes, \ + _dom_attr_clone_node, /*overload*/\ + _dom_node_normalize, \ + _dom_node_is_supported, \ + _dom_node_get_namespace, \ + _dom_node_get_prefix, \ + _dom_attr_set_prefix, /*overload*/\ + _dom_node_get_local_name, \ + _dom_node_has_attributes, \ + _dom_node_get_base, \ + _dom_node_compare_document_position, \ + _dom_node_get_text_content, \ + _dom_node_set_text_content, \ + _dom_node_is_same, \ + _dom_attr_lookup_prefix, /*overload*/\ + _dom_attr_is_default_namespace, /*overload*/\ + _dom_attr_lookup_namespace, /*overload*/\ + _dom_node_is_equal, \ + _dom_node_get_feature, \ + _dom_node_set_user_data, \ + _dom_node_get_user_data + +/* The protected virtual functions */ +void __dom_attr_destroy(dom_node_internal *node); +dom_exception _dom_attr_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret); +dom_exception _dom_attr_copy(struct dom_node_internal *new, + struct dom_node_internal *old); + +#define DOM_ATTR_PROTECT_VTABLE \ + __dom_attr_destroy, \ + _dom_attr_alloc, \ + _dom_attr_copy + + +inline void _dom_attr_set_isid(struct dom_attr *attr, bool is_id); +inline void _dom_attr_set_specified(struct dom_attr *attr, bool specified); + #endif diff --git a/src/core/cdatasection.c b/src/core/cdatasection.c index 71d3d43..b470df2 100644 --- a/src/core/cdatasection.c +++ b/src/core/cdatasection.c @@ -3,11 +3,13 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ #include "core/cdatasection.h" #include "core/document.h" #include "core/text.h" +#include "utils/utils.h" /** * A DOM CDATA section @@ -16,6 +18,10 @@ struct dom_cdata_section { struct dom_text base; /**< Base node */ }; +static struct dom_node_protect_vtable cdata_section_protect_vtable = { + DOM_CDATA_SECTION_PROTECT_VTABLE +}; + /** * Create a CDATA section * @@ -30,23 +36,27 @@ struct dom_cdata_section { * * The returned node will already be referenced. */ -dom_exception dom_cdata_section_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *value, +dom_exception _dom_cdata_section_create(struct dom_document *doc, + struct lwc_string_s *name, struct dom_string *value, struct dom_cdata_section **result) { struct dom_cdata_section *c; dom_exception err; /* Allocate the comment node */ - c = dom_document_alloc(doc, NULL, sizeof(struct dom_cdata_section)); + c = _dom_document_alloc(doc, NULL, sizeof(struct dom_cdata_section)); if (c == NULL) return DOM_NO_MEM_ERR; + + /* Set up vtable */ + ((dom_node_internal *) c)->base.vtable = &text_vtable; + ((dom_node_internal *) c)->vtable = &cdata_section_protect_vtable; /* And initialise the node */ - err = dom_text_initialise(&c->base, doc, DOM_CDATA_SECTION_NODE, - name, value); + err = _dom_cdata_section_initialise(&c->base, doc, + DOM_CDATA_SECTION_NODE, name, value); if (err != DOM_NO_ERR) { - dom_document_alloc(doc, c, 0); + _dom_document_alloc(doc, c, 0); return err; } @@ -63,12 +73,50 @@ dom_exception dom_cdata_section_create(struct dom_document *doc, * * The contents of ::cdata will be destroyed and ::cdata will be freed. */ -void dom_cdata_section_destroy(struct dom_document *doc, +void _dom_cdata_section_destroy(struct dom_document *doc, struct dom_cdata_section *cdata) { /* Clean up base node contents */ - dom_text_finalise(doc, &cdata->base); + _dom_cdata_section_finalise(doc, &cdata->base); /* Destroy the node */ - dom_document_alloc(doc, cdata, 0); + _dom_document_alloc(doc, cdata, 0); +} + +/*--------------------------------------------------------------------------*/ + +/* The protected virtual functions */ + +/* The virtual destroy function of this class */ +void __dom_cdata_section_destroy(struct dom_node_internal *node) +{ + struct dom_document *doc; + doc = dom_node_get_owner(node); + + _dom_cdata_section_destroy(doc, (struct dom_cdata_section *) node); +} + +/* The memory allocator of this class */ +dom_exception _dom_cdata_section_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + UNUSED(n); + dom_cdata_section *a; + + a = _dom_document_alloc(doc, NULL, sizeof(struct dom_cdata_section)); + if (a == NULL) + return DOM_NO_MEM_ERR; + + *ret = (dom_node_internal *) a; + dom_node_set_owner(*ret, doc); + + return DOM_NO_ERR; } + +/* The copy constructor of this class */ +dom_exception _dom_cdata_section_copy(struct dom_node_internal *new, + struct dom_node_internal *old) +{ + return _dom_characterdata_copy(new, old); +} + diff --git a/src/core/cdatasection.h b/src/core/cdatasection.h index 740c7d7..94d45a2 100644 --- a/src/core/cdatasection.h +++ b/src/core/cdatasection.h @@ -9,16 +9,34 @@ #define dom_internal_core_cdatasection_h_ #include +#include +struct dom_node_internal; struct dom_cdata_section; struct dom_document; struct dom_string; +struct lwc_string_s; -dom_exception dom_cdata_section_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *value, +dom_exception _dom_cdata_section_create(struct dom_document *doc, + struct lwc_string_s *name, struct dom_string *value, struct dom_cdata_section **result); -void dom_cdata_section_destroy(struct dom_document *doc, +void _dom_cdata_section_destroy(struct dom_document *doc, struct dom_cdata_section *cdata); +#define _dom_cdata_section_initialise _dom_text_initialise +#define _dom_cdata_section_finalise _dom_text_finalise + +/* Following comes the protected vtable */ +void __dom_cdata_section_destroy(struct dom_node_internal *node); +dom_exception _dom_cdata_section_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret); +dom_exception _dom_cdata_section_copy(struct dom_node_internal *new, + struct dom_node_internal *old); + +#define DOM_CDATA_SECTION_PROTECT_VTABLE \ + __dom_cdata_section_destroy, \ + _dom_cdata_section_alloc, \ + _dom_cdata_section_copy + #endif diff --git a/src/core/characterdata.c b/src/core/characterdata.c index 6beecbe..f5b4094 100644 --- a/src/core/characterdata.c +++ b/src/core/characterdata.c @@ -3,8 +3,11 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ +#include + #include #include @@ -13,28 +16,28 @@ #include "core/node.h" #include "utils/utils.h" -/* The virtual functions for dom_characterdata */ -static struct dom_characterdata_vtable characterdata_vtable = { +/* The virtual functions for dom_characterdata, we make this vtable + * public to each child class */ +struct dom_characterdata_vtable characterdata_vtable = { { DOM_NODE_VTABLE }, DOM_CHARACTERDATA_VTABLE }; -/** - * Create a DOM characterdata node and compose the vtable - * - * Return The new constructed DOM characterdata node of NULL if fail - */ -dom_characterdata *dom_characterdata_create(struct dom_document *doc) + +/* Create a DOM characterdata node and compose the vtable */ +dom_characterdata *_dom_characterdata_create(struct dom_document *doc) { - dom_characterdata *cdata = dom_document_alloc(doc, NULL, + dom_characterdata *cdata = _dom_document_alloc(doc, NULL, sizeof(struct dom_characterdata)); if (cdata == NULL) return NULL; cdata->base.base.vtable = &characterdata_vtable; + cdata->base.vtable = NULL; + return cdata; } @@ -50,11 +53,11 @@ dom_characterdata *dom_characterdata_create(struct dom_document *doc) * * ::doc, ::name and ::value will have their reference counts increased. */ -dom_exception dom_characterdata_initialise(struct dom_characterdata *cdata, +dom_exception _dom_characterdata_initialise(struct dom_characterdata *cdata, struct dom_document *doc, dom_node_type type, - struct dom_string *name, struct dom_string *value) + lwc_string *name, struct dom_string *value) { - return dom_node_initialise(&cdata->base, doc, type, + return _dom_node_initialise(&cdata->base, doc, type, name, value, NULL, NULL); } @@ -66,12 +69,17 @@ dom_exception dom_characterdata_initialise(struct dom_characterdata *cdata, * * The contents of ::cdata will be cleaned up. ::cdata will not be freed. */ -void dom_characterdata_finalise(struct dom_document *doc, +void _dom_characterdata_finalise(struct dom_document *doc, struct dom_characterdata *cdata) { - dom_node_finalise(doc, &cdata->base); + _dom_node_finalise(doc, &cdata->base); } + +/*----------------------------------------------------------------------*/ + +/* The public virtual functions */ + /** * Retrieve data from a character data node * @@ -182,7 +190,7 @@ dom_exception _dom_characterdata_substring_data( len = 0; } - if (offset >= len) { + if (offset > len) { return DOM_INDEX_SIZE_ERR; } @@ -210,9 +218,7 @@ dom_exception _dom_characterdata_append_data(struct dom_characterdata *cdata, return DOM_NO_MODIFICATION_ALLOWED_ERR; } - err = dom_string_insert(c->value, data, - c->value != NULL ? dom_string_length(c->value) : 0, - &temp); + err = dom_string_concat(c->value, data, &temp); if (err != DOM_NO_ERR) { return err; } @@ -255,7 +261,7 @@ dom_exception _dom_characterdata_insert_data(struct dom_characterdata *cdata, len = 0; } - if (offset >= len) { + if (offset > len) { return DOM_INDEX_SIZE_ERR; } @@ -302,7 +308,7 @@ dom_exception _dom_characterdata_delete_data(struct dom_characterdata *cdata, len = 0; } - if (offset >= len) { + if (offset > len) { return DOM_INDEX_SIZE_ERR; } @@ -353,7 +359,7 @@ dom_exception _dom_characterdata_replace_data(struct dom_characterdata *cdata, len = 0; } - if (offset >= len) { + if (offset > len) { return DOM_INDEX_SIZE_ERR; } @@ -373,3 +379,38 @@ dom_exception _dom_characterdata_replace_data(struct dom_characterdata *cdata, return DOM_NO_ERR; } + + +/*----------------------------------------------------------------------*/ + +/* The protected virtual functions of Node, see core/node.h for details + * + * @note: the three following API never be called directly from the virtual + * functions dispatch mechanism, they are here for the code consistent. + */ +void _dom_characterdata_destroy(struct dom_node_internal *node) +{ + assert("Should never be here" == NULL); + UNUSED(node); +} + +/* The memory allocator of this class */ +dom_exception _dom_characterdata_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + assert("Should never be here" == NULL); + UNUSED(doc); + UNUSED(n); + UNUSED(ret); + + return DOM_NO_ERR; +} + +/* The copy constructor of this class + * The sub-class of characterdata should call this API */ +dom_exception _dom_characterdata_copy(struct dom_node_internal *new, + struct dom_node_internal *old) +{ + return _dom_node_copy(new, old); +} + diff --git a/src/core/characterdata.h b/src/core/characterdata.h index 7076988..b5886ea 100644 --- a/src/core/characterdata.h +++ b/src/core/characterdata.h @@ -19,15 +19,17 @@ struct dom_characterdata { struct dom_node_internal base; /**< Base node */ }; -dom_characterdata *dom_characterdata_create(struct dom_document *doc); -dom_exception dom_characterdata_initialise(struct dom_characterdata *cdata, +/* The CharacterData is a intermediate node type, so the following function + * may never be used */ +dom_characterdata *_dom_characterdata_create(struct dom_document *doc); +dom_exception _dom_characterdata_initialise(struct dom_characterdata *cdata, struct dom_document *doc, dom_node_type type, - struct dom_string *name, struct dom_string *value); + struct lwc_string_s *name, struct dom_string *value); -void dom_characterdata_finalise(struct dom_document *doc, +void _dom_characterdata_finalise(struct dom_document *doc, struct dom_characterdata *cdata); -/* The virtual functions for characterdata */ +/* The virtual functions for dom_characterdata */ dom_exception _dom_characterdata_get_data(struct dom_characterdata *cdata, struct dom_string **data); dom_exception _dom_characterdata_set_data(struct dom_characterdata *cdata, @@ -57,4 +59,21 @@ dom_exception _dom_characterdata_replace_data(struct dom_characterdata *cdata, _dom_characterdata_delete_data, \ _dom_characterdata_replace_data +/* Following comes the protected vtable + * + * Only the _copy function can be used by sub-class of this. + */ +void _dom_characterdata_destroy(struct dom_node_internal *node); +dom_exception _dom_characterdata_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret); +dom_exception _dom_characterdata_copy(struct dom_node_internal *new, + struct dom_node_internal *old); + +#define DOM_CHARACTERDATA_PROTECT_VTABLE \ + _dom_characterdata_destroy, \ + _dom_characterdata_alloc, \ + _dom_characterdata_copy + +extern struct dom_characterdata_vtable characterdata_vtable; + #endif diff --git a/src/core/comment.c b/src/core/comment.c index 2e1c323..564bcd8 100644 --- a/src/core/comment.c +++ b/src/core/comment.c @@ -3,19 +3,26 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ #include "core/characterdata.h" #include "core/comment.h" #include "core/document.h" +#include "utils/utils.h" + /** - * A DOM comment node + * A DOM Comment node */ struct dom_comment { struct dom_characterdata base; /**< Base node */ }; +static struct dom_node_protect_vtable comment_protect_vtable = { + DOM_COMMENT_PROTECT_VTABLE +}; + /** * Create a comment node * @@ -30,23 +37,27 @@ struct dom_comment { * * The returned node will already be referenced. */ -dom_exception dom_comment_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *value, +dom_exception _dom_comment_create(struct dom_document *doc, + struct lwc_string_s *name, struct dom_string *value, struct dom_comment **result) { struct dom_comment *c; dom_exception err; /* Allocate the comment node */ - c = dom_document_alloc(doc, NULL, sizeof(struct dom_comment)); + c = _dom_document_alloc(doc, NULL, sizeof(struct dom_comment)); if (c == NULL) return DOM_NO_MEM_ERR; + /* Set the virtual table */ + ((dom_node_internal *) c)->base.vtable = &characterdata_vtable; + ((dom_node_internal *) c)->vtable = &comment_protect_vtable; + /* And initialise the node */ - err = dom_characterdata_initialise(&c->base, doc, DOM_COMMENT_NODE, + err = _dom_characterdata_initialise(&c->base, doc, DOM_COMMENT_NODE, name, value); if (err != DOM_NO_ERR) { - dom_document_alloc(doc, c, 0); + _dom_document_alloc(doc, c, 0); return err; } @@ -63,12 +74,50 @@ dom_exception dom_comment_create(struct dom_document *doc, * * The contents of ::comment will be destroyed and ::comment will be freed */ -void dom_comment_destroy(struct dom_document *doc, +void _dom_comment_destroy(struct dom_document *doc, struct dom_comment *comment) { /* Finalise base class contents */ - dom_characterdata_finalise(doc, &comment->base); + _dom_characterdata_finalise(doc, &comment->base); /* Free node */ - dom_document_alloc(doc, comment, 0); + _dom_document_alloc(doc, comment, 0); } + + +/*-----------------------------------------------------------------------*/ +/* The protected virtual functions */ + +/* The virtual destroy function */ +void __dom_comment_destroy(struct dom_node_internal *node) +{ + struct dom_document *doc; + doc = dom_node_get_owner(node); + + _dom_comment_destroy(doc, (struct dom_comment *) node); +} + +/* The memory allocation function of this class */ +dom_exception _dom_comment_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + UNUSED(n); + dom_comment *c; + + c = _dom_document_alloc(doc, NULL, sizeof(struct dom_comment)); + if (c == NULL) + return DOM_NO_MEM_ERR; + + *ret = (dom_node_internal *) c; + dom_node_set_owner(*ret, doc); + + return DOM_NO_ERR; +} + +/* The copy constructor of this class */ +dom_exception _dom_comment_copy(struct dom_node_internal *new, + struct dom_node_internal *old) +{ + return _dom_characterdata_copy(new, old); +} + diff --git a/src/core/comment.h b/src/core/comment.h index 38bb448..ef8791f 100644 --- a/src/core/comment.h +++ b/src/core/comment.h @@ -9,16 +9,33 @@ #define dom_internal_core_comment_h_ #include +#include struct dom_comment; struct dom_document; struct dom_string; +struct lwc_string_s; -dom_exception dom_comment_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *value, +dom_exception _dom_comment_create(struct dom_document *doc, + struct lwc_string_s *name, struct dom_string *value, struct dom_comment **result); -void dom_comment_destroy(struct dom_document *doc, +#define _dom_comment_initialise _dom_characterdata_initialise +#define _dom_comment_finalise _dom_characterdata_finalise + +void _dom_comment_destroy(struct dom_document *doc, struct dom_comment *comment); +/* Following comes the protected vtable */ +void __dom_comment_destroy(struct dom_node_internal *node); +dom_exception _dom_comment_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret); +dom_exception _dom_comment_copy(struct dom_node_internal *new, + struct dom_node_internal *old); + +#define DOM_COMMENT_PROTECT_VTABLE \ + __dom_comment_destroy, \ + _dom_comment_alloc, \ + _dom_comment_copy + #endif diff --git a/src/core/doc_fragment.c b/src/core/doc_fragment.c index d9eb272..6b46696 100644 --- a/src/core/doc_fragment.c +++ b/src/core/doc_fragment.c @@ -3,13 +3,17 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ +#include + #include #include "core/document.h" #include "core/doc_fragment.h" #include "core/node.h" +#include "utils/utils.h" /** * A DOM document fragment @@ -18,7 +22,13 @@ struct dom_document_fragment { struct dom_node_internal base; /**< Base node */ }; -void _dom_document_fragment_destroy(struct dom_node_internal *node); +static struct dom_node_vtable df_vtable = { + DOM_NODE_VTABLE +}; + +static struct dom_node_protect_vtable df_protect_vtable = { + DOM_DF_PROTECT_VTABLE +}; /** * Create a document fragment @@ -34,30 +44,31 @@ void _dom_document_fragment_destroy(struct dom_node_internal *node); * * The returned node will already be referenced. */ -dom_exception dom_document_fragment_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *value, +dom_exception _dom_document_fragment_create(struct dom_document *doc, + struct lwc_string_s *name, struct dom_string *value, struct dom_document_fragment **result) { struct dom_document_fragment *f; dom_exception err; /* Allocate the comment node */ - f = dom_document_alloc(doc, NULL, + f = _dom_document_alloc(doc, NULL, sizeof(struct dom_document_fragment)); if (f == NULL) return DOM_NO_MEM_ERR; + + f->base.base.vtable = &df_vtable; + f->base.vtable = &df_protect_vtable; + /* And initialise the node */ - err = dom_node_initialise(&f->base, doc, DOM_DOCUMENT_FRAGMENT_NODE, - name, value, NULL, NULL); + err = _dom_document_fragment_initialise(&f->base, doc, + DOM_DOCUMENT_FRAGMENT_NODE, name, value, NULL, NULL); if (err != DOM_NO_ERR) { - dom_document_alloc(doc, f, 0); + _dom_document_alloc(doc, f, 0); return err; } - /* Set the virtual function of destroy */ - f->base.destroy = &_dom_document_fragment_destroy; - *result = f; return DOM_NO_ERR; @@ -71,45 +82,49 @@ dom_exception dom_document_fragment_create(struct dom_document *doc, * * The contents of ::frag will be destroyed and ::frag will be freed. */ -void dom_document_fragment_destroy(struct dom_document *doc, +void _dom_document_fragment_destroy(struct dom_document *doc, struct dom_document_fragment *frag) { - struct dom_node_internal *c, *d; - - /* Destroy children of this node */ - for (c = frag->base.first_child; c != NULL; c = d) { - d = c->next; - - /* Detach child */ - c->parent = NULL; - - if (c->refcnt > 0) { - /* Something is using this child */ + /* Finalise base class */ + _dom_document_fragment_finalise(doc, &frag->base); - /** \todo add to list of nodes pending deletion */ + /* Destroy fragment */ + _dom_document_alloc(doc, frag, 0); +} - continue; - } +/*-----------------------------------------------------------------------*/ - /* Detach from sibling list */ - c->previous = NULL; - c->next = NULL; +/* Overload protected functions */ - dom_node_destroy(c); - } +/* The virtual destroy function of this class */ +void _dom_df_destroy(struct dom_node_internal *node) +{ + _dom_document_fragment_destroy(node->owner, + (struct dom_document_fragment *) node); +} - /* Finalise base class */ - dom_node_finalise(doc, &frag->base); +/* The memory allocator of this class */ +dom_exception _dom_df_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + UNUSED(n); + struct dom_document_fragment *a; + + a = _dom_document_alloc(doc, NULL, + sizeof(struct dom_document_fragment)); + if (a == NULL) + return DOM_NO_MEM_ERR; + + *ret = (dom_node_internal *) a; + dom_node_set_owner(*ret, doc); - /* Destroy fragment */ - dom_document_alloc(doc, frag, 0); + return DOM_NO_ERR; } -void _dom_document_fragment_destroy(struct dom_node_internal *node) +/* The copy constructor of this class */ +dom_exception _dom_df_copy(struct dom_node_internal *new, + struct dom_node_internal *old) { - struct dom_document *doc; - dom_node_get_owner_document(node, &doc); - - dom_document_fragment_destroy(doc, - (struct dom_document_fragment *) node); + return _dom_node_copy(new, old); } + diff --git a/src/core/doc_fragment.h b/src/core/doc_fragment.h index 08e3422..9d3de76 100644 --- a/src/core/doc_fragment.h +++ b/src/core/doc_fragment.h @@ -13,12 +13,29 @@ struct dom_document_fragment; struct dom_document; struct dom_string; +struct lwc_string_s; -dom_exception dom_document_fragment_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *value, +dom_exception _dom_document_fragment_create(struct dom_document *doc, + struct lwc_string_s *name, struct dom_string *value, struct dom_document_fragment **result); -void dom_document_fragment_destroy(struct dom_document *doc, +void _dom_document_fragment_destroy(struct dom_document *doc, struct dom_document_fragment *frag); +#define _dom_document_fragment_initialise _dom_node_initialise +#define _dom_document_fragment_finalise _dom_node_finalise + + +/* Following comes the protected vtable */ +void _dom_df_destroy(struct dom_node_internal *node); +dom_exception _dom_df_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret); +dom_exception _dom_df_copy(struct dom_node_internal *new, + struct dom_node_internal *old); + +#define DOM_DF_PROTECT_VTABLE \ + _dom_df_destroy, \ + _dom_df_alloc, \ + _dom_df_copy + #endif diff --git a/src/core/document.c b/src/core/document.c index 355d7f8..7d7e64a 100644 --- a/src/core/document.c +++ b/src/core/document.c @@ -3,16 +3,21 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ -#include +#include + +#include #include #include +#include +#include #include #include -#include +#include "core/string.h" #include "core/attr.h" #include "core/cdatasection.h" #include "core/comment.h" @@ -24,6 +29,7 @@ #include "core/nodelist.h" #include "core/pi.h" #include "core/text.h" +#include "utils/validate.h" #include "utils/namespace.h" #include "utils/utils.h" @@ -37,21 +43,6 @@ struct dom_doc_nl { struct dom_doc_nl *prev; /**< Previous item */ }; -/** - * Item 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 */ -}; - - -/** Interned node name strings, indexed by node type */ -/* Index 0 is unused */ -static struct dom_string *__nodenames_utf8[DOM_NODE_TYPE_COUNT + 1]; - /* The virtual functions of this dom_document */ static struct dom_document_vtable document_vtable = { { @@ -60,84 +51,31 @@ static struct dom_document_vtable document_vtable = { DOM_DOCUMENT_VTABLE }; -/** - * Initialise the document module - * - * \param alloc Memory (de)allocation function - * \param pw Pointer to client-specific private data - * \return DOM_NO_ERR on success - */ -dom_exception _dom_document_initialise(dom_alloc alloc, void *pw) -{ - static struct { - const char *name; - size_t len; - } names_utf8[DOM_NODE_TYPE_COUNT + 1] = { - { NULL, 0 }, /* Unused */ - { NULL, 0 }, /* Element */ - { NULL, 0 }, /* Attr */ - { "#text", 5 }, /* Text */ - { "#cdata-section", 14 }, /* CDATA section */ - { NULL, 0 }, /* Entity reference */ - { NULL, 0 }, /* Entity */ - { NULL, 0 }, /* Processing instruction */ - { "#comment", 8 }, /* Comment */ - { "#document", 9 }, /* Document */ - { NULL, 0 }, /* Document type */ - { "#document-fragment", 18 }, /* Document fragment */ - { NULL, 0 } /* Notation */ - }; - dom_exception err; +static struct dom_node_protect_vtable document_protect_vtable = { + DOM_DOCUMENT_PROTECT_VTABLE +}; - /* Initialise interned node names */ - for (int i = 0; i <= DOM_NODE_TYPE_COUNT; i++) { - if (names_utf8[i].name == NULL) { - /* Nothing to intern; skip this entry */ - __nodenames_utf8[i] = NULL; - continue; - } - /* Make string */ - err = dom_string_create(alloc, pw, - (const uint8_t *) names_utf8[i].name, - names_utf8[i].len, &__nodenames_utf8[i]); - if (err != DOM_NO_ERR) { - /* Failed, clean up strings we've created so far */ - for (int j = 0; j < i; j++) { - if (__nodenames_utf8[j] != NULL) { - dom_string_unref(__nodenames_utf8[j]); - } - } - return err; - } - } +/*----------------------------------------------------------------------*/ - return DOM_NO_ERR; -} +/* Internally used helper functions */ +static dom_exception dom_document_dup_node(dom_document *doc, + struct dom_node *node, bool deep, struct dom_node **result, + dom_node_operation opt); -/** - * Finalise the document module - * - * \return DOM_NO_ERR. - */ -dom_exception _dom_document_finalise(void) -{ - for (int i = 0; i <= DOM_NODE_TYPE_COUNT; i++) { - if (__nodenames_utf8[i] != NULL) { - dom_string_unref(__nodenames_utf8[i]); - } - } - return DOM_NO_ERR; -} +/*----------------------------------------------------------------------*/ + +/* The constructors and destructors */ /** * Create a Document * - * \param impl The DOM implementation owning the document - * \param alloc Memory (de)allocation function - * \param pw Pointer to client-specific private data - * \param doc Pointer to location to receive created document + * \param impl The DOM implementation owning the document + * \param alloc Memory (de)allocation function + * \param pw Pointer to client-specific private data + * \param doc Pointer to location to receive created document + * \param ctx The intern string context of this document * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion. * * ::impl will have its reference count increased. @@ -145,7 +83,8 @@ dom_exception _dom_document_finalise(void) * The returned document will already be referenced. */ dom_exception dom_document_create(struct dom_implementation *impl, - dom_alloc alloc, void *pw, struct dom_document **doc) + dom_alloc alloc, void *pw, struct lwc_context_s *ctx, + struct dom_document **doc) { struct dom_document *d; dom_exception err; @@ -155,103 +94,107 @@ dom_exception dom_document_create(struct dom_implementation *impl, if (d == NULL) return DOM_NO_MEM_ERR; - /* Set up document allocation context - must be first */ - d->alloc = alloc; - d->pw = pw; - /* Initialise the virtual table */ d->base.base.vtable = &document_vtable; - d->base.destroy = &dom_document_destroy; + d->base.vtable = &document_protect_vtable; /* Initialise base class -- the Document has no parent, so * destruction will be attempted as soon as its reference count * reaches zero. Documents own themselves (this simplifies the * rest of the code, as it doesn't need to special case Documents) */ - err = dom_node_initialise(&d->base, d, DOM_DOCUMENT_NODE, - NULL, NULL, NULL, NULL); + err = _dom_document_initialise(d, impl, alloc, pw, ctx); if (err != DOM_NO_ERR) { /* Clean up document */ alloc(d, 0, pw); return err; } - /* Initialise remaining type-specific data */ - if (impl != NULL) - dom_implementation_ref(impl); - d->impl = impl; - - d->nodelists = NULL; - d->maps = NULL; - - d->nodenames = __nodenames_utf8; - *doc = d; return DOM_NO_ERR; } -/** - * Destroy a document - * - * \param doc The document to destroy, which is passed in as a - * - * dom_node_internl - * - * The contents of ::doc will be destroyed and ::doc will be freed. - */ -void dom_document_destroy(struct dom_node_internal *dnode) +/* Initialise the document */ +dom_exception _dom_document_initialise(struct dom_document *doc, + struct dom_implementation *impl, dom_alloc alloc, void *pw, + struct lwc_context_s *ctx) { - struct dom_document *doc = (struct dom_document *) dnode; - struct dom_node_internal *c, *d; + assert(ctx != NULL); + assert(alloc != NULL); + assert(impl != NULL); - /* Destroy children of this node */ - for (c = doc->base.first_child; c != NULL; c = d) { - d = c->next; + dom_exception err; + lwc_string *name; + lwc_error lerr; + + lerr = lwc_context_intern(ctx, "#document", SLEN("#document"), &name); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); - /* Detach child */ - c->parent = NULL; + dom_implementation_ref(impl); + doc->impl = impl; - if (c->refcnt > 0) { - /* Something is using this child */ + doc->nodelists = NULL; - /** \todo add to list of nodes pending deletion */ + /* Set up document allocation context - must be first */ + doc->alloc = alloc; + doc->pw = pw; + doc->context = lwc_context_ref(ctx); - continue; - } + err = _dom_node_initialise(&doc->base, doc, DOM_DOCUMENT_NODE, + name, NULL, NULL, NULL); + lwc_context_string_unref(ctx, name); - /* Detach from sibling list */ - c->previous = NULL; - c->next = NULL; + list_init(&doc->pending_nodes); - dom_node_destroy(c); - } + doc->id_name = NULL; - /** \todo Ensure list of nodes pending deletion is empty. If not, + return err; +} + + +/* Finalise the document */ +bool _dom_document_finalise(struct dom_document *doc) +{ + /* Finalise base class, delete the tree in force */ + _dom_node_finalise(doc, &doc->base); + + /* Now, the first_child and last_child should be null */ + doc->base.first_child = NULL; + doc->base.last_child = NULL; + + /* Ensure list of nodes pending deletion is empty. If not, * then we can't yet destroy the document (its destruction will * have to wait until the pending nodes are destroyed) */ + if (doc->pending_nodes.next != &doc->pending_nodes) + return false; /* Ok, the document tree is empty, as is the list of nodes pending * deletion. Therefore, it is safe to destroy the document. */ - if (doc->impl != NULL) dom_implementation_unref(doc->impl); doc->impl = NULL; - /* This is paranoia -- if there are any remaining nodelists or - * namednodemaps, then the document's reference count will be + /* This is paranoia -- if there are any remaining nodelists, + * then the document's reference count will be * non-zero as these data structures reference the document because * they are held by the client. */ doc->nodelists = NULL; - doc->maps = NULL; - /* Finalise base class */ - dom_node_finalise(doc, &doc->base); + if (doc->id_name != NULL) + lwc_context_string_unref(doc->context, doc->id_name); + lwc_context_unref(doc->context); - /* Free document */ - doc->alloc(doc, 0, doc->pw); + return true; } + + +/*----------------------------------------------------------------------*/ + +/* Public virtual functions */ + /** * Retrieve the doctype of a document * @@ -319,7 +262,7 @@ dom_exception _dom_document_get_document_element(struct dom_document *doc, { struct dom_node_internal *root; - /* Find first element node in child list */ + /* Find the first element node in child list */ for (root = doc->base.first_child; root != NULL; root = root->next) { if (root->type == DOM_ELEMENT_NODE) break; @@ -351,7 +294,21 @@ dom_exception _dom_document_get_document_element(struct dom_document *doc, dom_exception _dom_document_create_element(struct dom_document *doc, struct dom_string *tag_name, struct dom_element **result) { - return dom_element_create(doc, tag_name, NULL, NULL, result); + lwc_string *name; + dom_exception err; + + if (_dom_validate_name(tag_name) == false) + return DOM_INVALID_CHARACTER_ERR; + + assert(doc->context != NULL); + err = _dom_string_intern(tag_name, doc->context, &name); + if (err != DOM_NO_ERR) + return err; + + err = _dom_element_create(doc, name, NULL, NULL, result); + lwc_context_string_unref(doc->context, name); + + return err; } /** @@ -368,9 +325,21 @@ dom_exception _dom_document_create_element(struct dom_document *doc, dom_exception _dom_document_create_document_fragment(struct dom_document *doc, struct dom_document_fragment **result) { - return dom_document_fragment_create(doc, - doc->nodenames[DOM_DOCUMENT_FRAGMENT_NODE], - NULL, result); + lwc_string *name; + dom_exception err; + lwc_error lerr; + + assert(doc->context != NULL); + + lerr = lwc_context_intern(doc->context, "#document-fragment", + SLEN("#document-fragment"), &name); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); + + err = _dom_document_fragment_create(doc, name, NULL, result); + lwc_context_string_unref(doc->context, name); + + return err; } /** @@ -388,8 +357,20 @@ dom_exception _dom_document_create_document_fragment(struct dom_document *doc, dom_exception _dom_document_create_text_node(struct dom_document *doc, struct dom_string *data, struct dom_text **result) { - return dom_text_create(doc, doc->nodenames[DOM_TEXT_NODE], - data, result); + lwc_string *name; + dom_exception err; + lwc_error lerr; + + assert(doc->context != NULL); + + lerr = lwc_context_intern(doc->context, "#text", SLEN("#text"), &name); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); + + err = _dom_text_create(doc, name, data, result); + lwc_context_string_unref(doc->context, name); + + return err; } /** @@ -407,8 +388,21 @@ dom_exception _dom_document_create_text_node(struct dom_document *doc, dom_exception _dom_document_create_comment(struct dom_document *doc, struct dom_string *data, struct dom_comment **result) { - return dom_comment_create(doc, doc->nodenames[DOM_COMMENT_NODE], - data, result); + lwc_string *name; + dom_exception err; + lwc_error lerr; + + assert(doc->context != NULL); + + lerr = lwc_context_intern(doc->context, "#comment", SLEN("#comment"), + &name); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); + + err = _dom_comment_create(doc, name, data, result); + lwc_context_string_unref(doc->context, name); + + return err; } /** @@ -427,9 +421,21 @@ dom_exception _dom_document_create_comment(struct dom_document *doc, dom_exception _dom_document_create_cdata_section(struct dom_document *doc, struct dom_string *data, struct dom_cdata_section **result) { - return dom_cdata_section_create(doc, - doc->nodenames[DOM_CDATA_SECTION_NODE], - data, result); + lwc_string *name; + dom_exception err; + lwc_error lerr; + + assert(doc->context != NULL); + + lerr = lwc_context_intern(doc->context, "#cdata-section", + SLEN("#cdata-section"), &name); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); + + err = _dom_cdata_section_create(doc, name, data, result); + lwc_context_string_unref(doc->context, name); + + return err; } /** @@ -452,7 +458,21 @@ dom_exception _dom_document_create_processing_instruction( struct dom_string *data, struct dom_processing_instruction **result) { - return dom_processing_instruction_create(doc, target, data, result); + lwc_string *name; + dom_exception err; + + if (_dom_validate_name(target) == false) + return DOM_INVALID_CHARACTER_ERR; + + assert(doc->context != NULL); + err = _dom_string_intern(target, doc->context, &name); + if (err != DOM_NO_ERR) + return err; + + err = _dom_processing_instruction_create(doc, name, data, result); + lwc_context_string_unref(doc->context, name); + + return err; } /** @@ -471,7 +491,20 @@ dom_exception _dom_document_create_processing_instruction( dom_exception _dom_document_create_attribute(struct dom_document *doc, struct dom_string *name, struct dom_attr **result) { - return dom_attr_create(doc, name, NULL, NULL, result); + lwc_string *n; + dom_exception err; + + if (_dom_validate_name(name) == false) + return DOM_INVALID_CHARACTER_ERR; + + assert(doc->context != NULL); + err = _dom_string_intern(name, doc->context, &n); + if (err != DOM_NO_ERR) + return err; + + err = _dom_attr_create(doc, n, NULL, NULL, true, result); + lwc_context_string_unref(doc->context, n); + return err; } /** @@ -492,7 +525,20 @@ dom_exception _dom_document_create_entity_reference(struct dom_document *doc, struct dom_string *name, struct dom_entity_reference **result) { - return dom_entity_reference_create(doc, name, NULL, result); + lwc_string *n; + dom_exception err; + + if (_dom_validate_name(name) == false) + return DOM_INVALID_CHARACTER_ERR; + + assert(doc->context != NULL); + err = _dom_string_intern(name, doc->context, &n); + if (err != DOM_NO_ERR) + return err; + + err = _dom_entity_reference_create(doc, n, NULL, result); + lwc_context_string_unref(doc->context, n); + return err; } /** @@ -510,8 +556,20 @@ dom_exception _dom_document_create_entity_reference(struct dom_document *doc, dom_exception _dom_document_get_elements_by_tag_name(struct dom_document *doc, struct dom_string *tagname, struct dom_nodelist **result) { - return dom_document_get_nodelist(doc, (struct dom_node_internal *) doc, - tagname, NULL, NULL, result); + lwc_string *name; + dom_exception err; + + assert(doc->context != NULL); + err = _dom_string_intern(tagname, doc->context, &name); + if (err != DOM_NO_ERR) + return err; + + err = _dom_document_get_nodelist(doc, DOM_NODELIST_BY_NAME, + (struct dom_node_internal *) doc, name, NULL, NULL, + result); + lwc_context_string_unref(doc->context, name); + + return err; } /** @@ -532,12 +590,10 @@ dom_exception _dom_document_get_elements_by_tag_name(struct dom_document *doc, dom_exception _dom_document_import_node(struct dom_document *doc, struct dom_node *node, bool deep, struct dom_node **result) { - UNUSED(doc); - UNUSED(node); - UNUSED(deep); - UNUSED(result); + /* TODO: The DOM_INVALID_CHARACTER_ERR exception */ - return DOM_NOT_SUPPORTED_ERR; + return dom_document_dup_node(doc, node, deep, result, + DOM_NODE_IMPORTED); } /** @@ -575,7 +631,8 @@ dom_exception _dom_document_create_element_ns(struct dom_document *doc, struct dom_string *prefix, *localname; dom_exception err; - /** \todo ensure document supports XML feature */ + if (_dom_validate_name(qname) == false) + return DOM_INVALID_CHARACTER_ERR; /* Validate qname */ err = _dom_namespace_validate_qname(qname, namespace); @@ -589,13 +646,57 @@ dom_exception _dom_document_create_element_ns(struct dom_document *doc, return err; } + /* Get the interned string from the dom_string */ + assert(doc->context != NULL); + lwc_string *l = NULL, *n = NULL, *p = NULL; + if (localname != NULL) { + err = _dom_string_intern(localname, doc->context, &l); + if (err != DOM_NO_ERR) { + dom_string_unref(localname); + if (prefix != NULL) + dom_string_unref(prefix); + + 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); + dom_string_unref(localname); + if (prefix != NULL) + dom_string_unref(prefix); + + return err; + } + } + if (prefix != NULL) { + err = _dom_string_intern(prefix, doc->context, &p); + if (err != DOM_NO_ERR) { + lwc_context_string_unref(doc->context, l); + lwc_context_string_unref(doc->context, n); + dom_string_unref(localname); + if (prefix != NULL) + dom_string_unref(prefix); + + return err; + } + } + /* Attempt to create element */ - err = dom_element_create(doc, localname, namespace, prefix, result); + err = _dom_element_create(doc, l, n, p, result); /* Tidy up */ - dom_string_unref(localname); + if (localname != NULL) { + dom_string_unref(localname); + lwc_context_string_unref(doc->context, l); + } if (prefix != NULL) { dom_string_unref(prefix); + lwc_context_string_unref(doc->context, p); + } + if (namespace != NULL) { + lwc_context_string_unref(doc->context, n); } return err; @@ -636,7 +737,8 @@ dom_exception _dom_document_create_attribute_ns(struct dom_document *doc, struct dom_string *prefix, *localname; dom_exception err; - /** \todo ensure document supports XML feature */ + if (_dom_validate_name(qname) == false) + return DOM_INVALID_CHARACTER_ERR; /* Validate qname */ err = _dom_namespace_validate_qname(qname, namespace); @@ -650,13 +752,56 @@ dom_exception _dom_document_create_attribute_ns(struct dom_document *doc, return err; } + /* Get the interned string from the dom_string */ + assert(doc->context != NULL); + lwc_string *l = NULL, *n = NULL, *p = NULL; + if (localname != NULL) { + err = _dom_string_intern(localname, doc->context, &l); + if (err != DOM_NO_ERR) { + dom_string_unref(localname); + if (prefix != NULL) + dom_string_unref(prefix); + + 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); + dom_string_unref(localname); + if (prefix != NULL) + dom_string_unref(prefix); + + return err; + } + } + if (prefix != NULL) { + err = _dom_string_intern(prefix, doc->context, &p); + if (err != DOM_NO_ERR) { + lwc_context_string_unref(doc->context, l); + lwc_context_string_unref(doc->context, n); + dom_string_unref(localname); + if (prefix != NULL) + dom_string_unref(prefix); + + return err; + } + } /* Attempt to create attribute */ - err = dom_attr_create(doc, localname, namespace, prefix, result); + err = _dom_attr_create(doc, l, n, p, true, result); /* Tidy up */ - dom_string_unref(localname); + if (localname != NULL) { + dom_string_unref(localname); + lwc_context_string_unref(doc->context, l); + } if (prefix != NULL) { dom_string_unref(prefix); + lwc_context_string_unref(doc->context, p); + } + if (namespace != NULL) { + lwc_context_string_unref(doc->context, n); } return err; @@ -669,7 +814,7 @@ dom_exception _dom_document_create_attribute_ns(struct dom_document *doc, * \param namespace The namespace URI * \param localname The local name * \param result Pointer to location to receive result - * \return DOM_NO_ERR. + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. * * The returned list will have its reference count increased. It is * the responsibility of the caller to unref the list once it has @@ -679,8 +824,33 @@ dom_exception _dom_document_get_elements_by_tag_name_ns( struct dom_document *doc, struct dom_string *namespace, struct dom_string *localname, struct dom_nodelist **result) { - return dom_document_get_nodelist(doc, (struct dom_node_internal *) doc, - NULL, namespace, localname, result); + dom_exception err; + lwc_string *l = NULL, *n = NULL; + + /* Get the interned string from the dom_string */ + assert(doc->context != 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(doc, DOM_NODELIST_BY_NAMESPACE, + (struct dom_node_internal *) doc, NULL, n, l, result); + + if (l != NULL) + lwc_context_string_unref(doc->context, l); + if (n != NULL) + lwc_context_string_unref(doc->context, n); + + return err; } /** @@ -689,7 +859,7 @@ dom_exception _dom_document_get_elements_by_tag_name_ns( * \param doc The document to search in * \param id The ID to search for * \param result Pointer to location to receive result - * \return DOM_NO_ERR. + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. * * The returned node will have its reference count increased. It is * the responsibility of the caller to unref the node once it has @@ -698,11 +868,25 @@ dom_exception _dom_document_get_elements_by_tag_name_ns( dom_exception _dom_document_get_element_by_id(struct dom_document *doc, struct dom_string *id, struct dom_element **result) { - UNUSED(doc); - UNUSED(id); - UNUSED(result); + lwc_string *i; + dom_node_internal *root; + dom_exception err; - return DOM_NOT_SUPPORTED_ERR; + *result = NULL; + + assert(doc->context != NULL); + err = _dom_string_intern(id, doc->context, &i); + if (err != DOM_NO_ERR) + return err; + + err = dom_document_get_document_element(doc, (void *) &root); + if (err != DOM_NO_ERR) + return err; + + err = _dom_find_element_by_id(root, i, result); + dom_node_unref(root); + + return err; } /** @@ -710,7 +894,7 @@ dom_exception _dom_document_get_element_by_id(struct dom_document *doc, * * \param doc The document to query * \param result Pointer to location to receive result - * \return DOM_NO_ERR. + * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. * * The returned string will have its reference count increased. It is * the responsibility of the caller to unref the string once it has @@ -730,7 +914,7 @@ dom_exception _dom_document_get_input_encoding(struct dom_document *doc, * * \param doc The document to query * \param result Pointer to location to receive result - * \return DOM_NO_ERR. + * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. * * The returned string will have its reference count increased. It is * the responsibility of the caller to unref the string once it has @@ -750,7 +934,7 @@ dom_exception _dom_document_get_xml_encoding(struct dom_document *doc, * * \param doc The document to query * \param result Pointer to location to receive result - * \return DOM_NO_ERR. + * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. */ dom_exception _dom_document_get_xml_standalone(struct dom_document *doc, bool *result) @@ -769,6 +953,9 @@ dom_exception _dom_document_get_xml_standalone(struct dom_document *doc, * \return DOM_NO_ERR on success, * DOM_NOT_SUPPORTED_ERR if the document does not support the "XML" * feature. + * + * We don't support this API now, so the return value is always + * DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_set_xml_standalone(struct dom_document *doc, bool standalone) @@ -784,11 +971,14 @@ dom_exception _dom_document_set_xml_standalone(struct dom_document *doc, * * \param doc The document to query * \param result Pointer to location to receive result - * \return DOM_NO_ERR. + * \return DOM_NO_ERR * * The returned string will have its reference count increased. It is * the responsibility of the caller to unref the string once it has * finished with it. + * + * We don't support this API now, so the return value is always + * DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_get_xml_version(struct dom_document *doc, struct dom_string **result) @@ -807,6 +997,9 @@ dom_exception _dom_document_get_xml_version(struct dom_document *doc, * \return DOM_NO_ERR on success, * DOM_NOT_SUPPORTED_ERR if the document does not support the "XML" * feature. + * + * We don't support this API now, so the return value is always + * DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_set_xml_version(struct dom_document *doc, struct dom_string *version) @@ -822,7 +1015,7 @@ dom_exception _dom_document_set_xml_version(struct dom_document *doc, * * \param doc The document to query * \param result Pointer to location to receive result - * \return DOM_NO_ERR. + * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. */ dom_exception _dom_document_get_strict_error_checking( struct dom_document *doc, bool *result) @@ -838,7 +1031,7 @@ dom_exception _dom_document_get_strict_error_checking( * * \param doc The document to query * \param strict Whether to use strict error checking - * \return DOM_NO_ERR. + * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. */ dom_exception _dom_document_set_strict_error_checking( struct dom_document *doc, bool strict) @@ -863,10 +1056,10 @@ dom_exception _dom_document_set_strict_error_checking( dom_exception _dom_document_get_uri(struct dom_document *doc, struct dom_string **result) { - UNUSED(doc); - UNUSED(result); + dom_string_ref(doc->uri); + *result = doc->uri; - return DOM_NOT_SUPPORTED_ERR; + return DOM_NO_ERR; } /** @@ -883,10 +1076,11 @@ dom_exception _dom_document_get_uri(struct dom_document *doc, dom_exception _dom_document_set_uri(struct dom_document *doc, struct dom_string *uri) { - UNUSED(doc); - UNUSED(uri); + dom_string_unref(doc->uri); + dom_string_ref(uri); + doc->uri = uri; - return DOM_NOT_SUPPORTED_ERR; + return DOM_NO_ERR; } /** @@ -903,15 +1097,62 @@ dom_exception _dom_document_set_uri(struct dom_document *doc, * 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. + * + * @note: The spec said adoptNode may be light weight than the importNode + * because the former need no Node creation. But in our implementation + * this can't be ensured. Both adoptNode and importNode create new + * nodes using the importing/adopting document's resource manager. So, + * generally, the adoptNode and importNode call the same function + * dom_document_dup_node. */ dom_exception _dom_document_adopt_node(struct dom_document *doc, struct dom_node *node, struct dom_node **result) { - UNUSED(doc); - UNUSED(node); - UNUSED(result); + dom_exception err; + dom_node_internal *n = (dom_node_internal *) node; + + *result = NULL; - return DOM_NOT_SUPPORTED_ERR; + if (n->type == DOM_DOCUMENT_NODE || + n->type == DOM_DOCUMENT_TYPE_NODE) { + return DOM_NOT_SUPPORTED_ERR; + } + + if (n->type == DOM_ENTITY_NODE || + n->type == DOM_NOTATION_NODE || + n->type == DOM_PROCESSING_INSTRUCTION_NODE || + n->type == DOM_TEXT_NODE || + n->type == DOM_CDATA_SECTION_NODE || + n->type == DOM_COMMENT_NODE) { + *result = NULL; + return DOM_NO_ERR; + } + + /* Support XML when necessary */ + if (n->type == DOM_ENTITY_REFERENCE_NODE) { + return DOM_NOT_SUPPORTED_ERR; + } + + err = dom_document_dup_node(doc, node, true, result, DOM_NODE_ADOPTED); + if (err != DOM_NO_ERR) { + *result = NULL; + return err; + } + + dom_node_internal *parent = n->parent; + dom_node_internal *tmp; + if (parent != NULL) { + err = dom_node_remove_child(parent, node, (void *) &tmp); + if (err != DOM_NO_ERR) { + dom_node_unref(*result); + *result = NULL; + return err; + } + } + + dom_node_unref(tmp); + + return DOM_NO_ERR; } /** @@ -919,7 +1160,7 @@ dom_exception _dom_document_adopt_node(struct dom_document *doc, * * \param doc The document to query * \param result Pointer to location to receive result - * \return DOM_NO_ERR. + * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. * * The returned object will have its reference count increased. It is * the responsibility of the caller to unref the object once it has @@ -938,7 +1179,7 @@ dom_exception _dom_document_get_dom_config(struct dom_document *doc, * Normalize a document * * \param doc The document to normalize - * \return DOM_NO_ERR. + * \return DOM_NOT_SUPPORTED_ERR, we don't support this API now. */ dom_exception _dom_document_normalize(struct dom_document *doc) { @@ -977,6 +1218,9 @@ dom_exception _dom_document_normalize(struct dom_document *doc) * 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. + * + * We don't support this API now, so the return value is always + * DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_rename_node(struct dom_document *doc, struct dom_node *node, @@ -992,6 +1236,51 @@ dom_exception _dom_document_rename_node(struct dom_document *doc, return DOM_NOT_SUPPORTED_ERR; } +/*-----------------------------------------------------------------------*/ + +/* Overload protectd virtual functions */ + +/* The virtual destroy function of this class */ +void _dom_document_destroy(struct dom_node_internal *node) +{ + struct dom_document *doc = (struct dom_document *) node; + + if (_dom_document_finalise(doc) == true) { + doc->alloc(doc, 0, doc->pw); + } +} + +/* The memory allocation function of this class */ +dom_exception __dom_document_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + UNUSED(n); + struct dom_document *a; + + a = _dom_document_alloc(doc, NULL, sizeof(struct dom_document)); + if (a == NULL) + return DOM_NO_MEM_ERR; + + *ret = (dom_node_internal *) a; + dom_node_set_owner(*ret, doc); + + return DOM_NO_ERR; +} + +/* The copy constructor function of this class */ +dom_exception _dom_document_copy(struct dom_node_internal *new, + struct dom_node_internal *old) +{ + UNUSED(new); + UNUSED(old); + + return DOM_NOT_SUPPORTED_ERR; +} + + +/* ----------------------------------------------------------------------- */ + +/* Helper functions */ /** * Create a DOM string, using a document's allocation context * @@ -1007,15 +1296,96 @@ dom_exception _dom_document_rename_node(struct dom_document *doc, * The string of characters passed in will be copied for use by the * returned DOM string. */ -dom_exception dom_document_create_string(struct dom_document *doc, +dom_exception _dom_document_create_string(struct dom_document *doc, const uint8_t *data, size_t len, struct dom_string **result) { return dom_string_create(doc->alloc, doc->pw, data, len, result); } -/* */ -/* ----------------------------------------------------------------------- */ -/* */ +/* + * Create a lwc_string + * + * \param doc The document object + * \param data The raw string data + * \param len The raw string length + * \param result The resturned lwc_string + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_document_create_lwcstring(struct dom_document *doc, + const uint8_t *data, size_t len, struct lwc_string_s **result) +{ + lwc_error lerr; + + assert(doc->context != NULL); + + lerr = lwc_context_intern(doc->context, (const char *) data, len, + result); + + return _dom_exception_from_lwc_error(lerr); +} + +/* Simple accessor for lwc_context of this document */ +struct lwc_context_s *_dom_document_get_intern_context( + struct dom_document *doc) +{ + return doc->context; +} + +/* Get the resource manager from the document */ +void _dom_document_get_resource_mgr( + struct dom_document *doc, struct dom_resource_mgr *rm) +{ + rm->alloc = doc->alloc; + rm->pw = doc->pw; + rm->ctx = doc->context; +} + +/* Simple accessor for allocator data for this document */ +void _dom_document_get_allocator(struct dom_document *doc, dom_alloc *al, + void **pw) +{ + *al = doc->alloc; + *pw = doc->pw; +} +/* + * Create a dom_string from a lwc_string. + * + * \param doc The document object + * \param str The lwc_string object + * \param result The retured dom_string + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_document_create_string_from_lwcstring( + struct dom_document *doc, struct lwc_string_s *str, + struct dom_string **result) +{ + assert(doc->context != NULL); + + return _dom_string_create_from_lwcstring(doc->alloc, doc->pw, + doc->context, str, result); +} + +/** + * Create a hash_table + * + * \param doc The dom_document + * \param chains The number of chains + * \param f The hash function + * \param ht The returned hash_table + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_document_create_hashtable(struct dom_document *doc, + size_t chains, dom_hash_func f, struct dom_hash_table **ht) +{ + struct dom_hash_table *ret; + + ret = _dom_hash_create(chains, f, doc->alloc, doc->pw); + if (ret == NULL) + return DOM_NO_MEM_ERR; + + *ht = ret; + return DOM_NO_ERR; +} /** * (De)allocate memory with a document's context @@ -1028,7 +1398,7 @@ dom_exception dom_document_create_string(struct dom_document *doc, * This call (modulo ::doc) has the same semantics as realloc(). * It is a thin veneer over the client-provided allocation function. */ -void *dom_document_alloc(struct dom_document *doc, void *ptr, size_t size) +void *_dom_document_alloc(struct dom_document *doc, void *ptr, size_t size) { return doc->alloc(ptr, size, doc->pw); } @@ -1037,6 +1407,7 @@ void *dom_document_alloc(struct dom_document *doc, void *ptr, size_t size) * Get a nodelist, creating one if necessary * * \param doc The document to get a nodelist for + * \param type The type of the NodeList * \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) @@ -1048,16 +1419,16 @@ void *dom_document_alloc(struct dom_document *doc, void *ptr, size_t size) * 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_internal *root, struct dom_string *tagname, - struct dom_string *namespace, struct dom_string *localname, - struct dom_nodelist **list) +dom_exception _dom_document_get_nodelist(struct dom_document *doc, + nodelist_type type, struct dom_node_internal *root, + struct lwc_string_s *tagname, struct lwc_string_s *namespace, + struct lwc_string_s *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, + if (_dom_nodelist_match(l->list, type, root, tagname, namespace, localname)) break; } @@ -1074,7 +1445,7 @@ dom_exception dom_document_get_nodelist(struct dom_document *doc, return DOM_NO_MEM_ERR; /* Create nodelist */ - err = dom_nodelist_create(doc, root, tagname, namespace, + err = _dom_nodelist_create(doc, type, root, tagname, namespace, localname, &l->list); if (err != DOM_NO_ERR) { doc->alloc(l, 0, doc->pw); @@ -1093,7 +1464,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; @@ -1106,7 +1477,7 @@ dom_exception dom_document_get_nodelist(struct dom_document *doc, * \param doc The document to remove the list from * \param list The list to remove */ -void dom_document_remove_nodelist(struct dom_document *doc, +void _dom_document_remove_nodelist(struct dom_document *doc, struct dom_nodelist *list) { struct dom_doc_nl *l; @@ -1135,97 +1506,175 @@ void dom_document_remove_nodelist(struct dom_document *doc, } /** - * Get a namednodemap, creating one if necessary + * Find element with certain ID in the subtree rooted at root * - * \param doc The document to get a namednodemap for - * \param head Start of list containing items in map - * \param type The type of items in 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. + * \param root The root element from where we start + * \param id The ID of the target element + * \param result The result element + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. */ -dom_exception dom_document_get_namednodemap(struct dom_document *doc, - struct dom_node_internal *head, dom_node_type type, - struct dom_namednodemap **map) +dom_exception _dom_find_element_by_id(dom_node_internal *root, + struct lwc_string_s *id, struct dom_element **result) +{ + *result = NULL; + dom_node_internal *node = root; + + while (node != NULL) { + if (root->type == DOM_ELEMENT_NODE) { + lwc_string *real_id; + _dom_element_get_id((dom_element *) node, &real_id); + if (real_id == id) { + *result = (dom_element *) node; + return DOM_NO_ERR; + } + } + + if (node->first_child != NULL) { + /* Has children */ + node = node->first_child; + } else if (node->next != NULL) { + /* No children, but has siblings */ + node = node->next; + } else { + /* No children or siblings. + * Find first unvisited relation. */ + struct dom_node_internal *parent = node->parent; + + while (parent != root && + node == parent->last_child) { + node = parent; + parent = parent->parent; + } + + node = node->next; + } + } + + return DOM_NO_ERR; +} + +/** + * Duplicate a Node + * + * \param doc The documen + * \param node The node to duplicate + * \param deep Whether to make a deep copy + * \param result The returned node + * \param opt Whether this is adopt or import operation + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception dom_document_dup_node(dom_document *doc, struct dom_node *node, + bool deep, struct dom_node **result, dom_node_operation opt) { - struct dom_doc_nnm *m; dom_exception err; + dom_node_internal *n = (dom_node_internal *) node; - for (m = doc->maps; m; m = m->next) { - if (dom_namednodemap_match(m->map, head, type)) - break; + if (opt == DOM_NODE_ADOPTED && _dom_node_readonly(n)) + return DOM_NO_MODIFICATION_ALLOWED_ERR; + + if (n->type == DOM_DOCUMENT_NODE || + n->type == DOM_DOCUMENT_TYPE_NODE) + return DOM_NOT_SUPPORTED_ERR; + + err = dom_node_alloc(doc, node, result); + if (err != DOM_NO_ERR) + return err; + + err = dom_node_copy(*result, node); + if (err != DOM_NO_ERR) { + _dom_document_alloc(doc, *result, 0); + return err; } - if (m != NULL) { - /* Found an existing map, so use it */ - dom_namednodemap_ref(m->map); - } else { - /* No existing map */ + if (n->type == DOM_ATTRIBUTE_NODE) { + _dom_attr_set_specified((dom_attr *) node, true); + deep = true; + } - /* Create active map entry */ - m = doc->alloc(NULL, sizeof(struct dom_doc_nnm), doc->pw); - if (m == NULL) - return DOM_NO_MEM_ERR; + if (n->type == DOM_ENTITY_REFERENCE_NODE) { + deep = false; + } - /* Create namednodemap */ - err = dom_namednodemap_create(doc, head, type, &m->map); - if (err != DOM_NO_ERR) { - doc->alloc(m, 0, doc->pw); - return err; - } + if (n->type == DOM_ELEMENT_NODE) { + /* Specified attributes are copyied but not default attributes, + * if the document object hold all the default attributes, we + * have nothing to do here */ + } - /* 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; + if (opt == DOM_NODE_ADOPTED && (n->type == DOM_ENTITY_NODE || + n->type == DOM_NOTATION_NODE)) { + /* We did not support XML now */ + return DOM_NOT_SUPPORTED_ERR; } - /* 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. */ + dom_node_internal *child, *r; + if (deep == true) { + child = ((dom_node_internal *) node)->first_child; + while (child != NULL) { + err = dom_document_import_node(doc, child, deep, + (void *) &r); + if (err != DOM_NO_ERR) { + _dom_document_alloc(doc, *result, 0); + return err; + } - *map = m->map; + err = dom_node_append_child(*result, r, (void *) &r); + if (err != DOM_NO_ERR) { + _dom_document_alloc(doc, *result, 0); + dom_node_unref(r); + return err; + } + dom_node_unref(r); + + child = child->next; + } + } + + /* Call the dom_user_data_handlers */ + dom_user_data *ud; + ud = n->user_data; + while (ud != NULL) { + if (ud->handler != NULL) + ud->handler(opt, ud->key, ud->data, + node, *result); + ud = ud->next; + } return DOM_NO_ERR; } /** - * Remove a namednodemap + * Try to destory the document. + * + * \param doc The instance of Document + * + * Delete the document if: + * 1. The refcnt reach zero + * 2. The pending list is empty * - * \param doc The document to remove the map from - * \param map The map to remove + * else, do nothing. */ -void dom_document_remove_namednodemap(struct dom_document *doc, - struct dom_namednodemap *map) +void _dom_document_try_destroy(struct dom_document *doc) { - 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 */ + if (doc->base.refcnt != 0 || doc->base.parent != NULL) 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; + _dom_document_destroy((dom_node_internal *) doc); +} - /* And free item */ - doc->alloc(m, 0, doc->pw); +/** + * Set the ID attribute name of this document + * + * \param doc The document object + * \param name The ID name of the elements in this document + * + * @note: The lwc_context of the param 'name' must be the same one with + * document's, this should be assured by the client. + */ +void _dom_document_set_id_name(dom_document *doc, struct lwc_string_s *name) +{ + if (doc->id_name != NULL) + lwc_context_string_unref(doc->context, doc->id_name); + doc->id_name = lwc_context_string_ref(doc->context, name); } + diff --git a/src/core/document.h b/src/core/document.h index f05b9e0..145eddf 100644 --- a/src/core/document.h +++ b/src/core/document.h @@ -12,9 +12,14 @@ #include #include -#include +#include "core/string.h" #include "core/node.h" +#include "core/nodelist.h" + +#include "utils/hashtable.h" +#include "utils/resource_mgr.h" +#include "utils/list.h" struct dom_document; struct dom_namednodemap; @@ -32,7 +37,6 @@ struct dom_entity_reference; struct dom_configuration; struct dom_doc_nl; -struct dom_doc_nnm; /** * DOM document @@ -46,14 +50,40 @@ struct dom_document { struct dom_doc_nl *nodelists; /**< List of active nodelists */ - struct dom_doc_nnm *maps; /**< List of active namednodemaps */ + struct dom_string *uri; /**< The uri of this document */ - struct dom_string **nodenames; /**< Interned nodenames */ + struct lwc_context_s *context; /**< The internment context */ dom_alloc alloc; /**< Memory (de)allocation function */ void *pw; /**< Pointer to client data */ + + struct list_entry pending_nodes; + /**< The deletion pending list */ + + struct lwc_string_s *id_name; /**< The ID attribute's name */ }; +/* Initialise the document */ +dom_exception _dom_document_initialise(struct dom_document *doc, + struct dom_implementation *impl, dom_alloc alloc, void *pw, + struct lwc_context_s *ctx); + +/* Finalise the document */ +bool _dom_document_finalise(struct dom_document *doc); + +/* Create a dom_string from C string */ +dom_exception _dom_document_create_string(struct dom_document *doc, + const uint8_t *data, size_t len, struct dom_string **result); +/* Create a lwc_string from C string */ +dom_exception _dom_document_create_lwcstring(struct dom_document *doc, + const uint8_t *data, size_t len, struct lwc_string_s **result); +/* Create a dom_string from a lwc_string */ +dom_exception _dom_document_create_string_from_lwcstring( + struct dom_document *doc, struct lwc_string_s *str, + struct dom_string **result); + + +/* Begin the virtual functions */ dom_exception _dom_document_get_doctype(struct dom_document *doc, struct dom_document_type **result); dom_exception _dom_document_get_implementation(struct dom_document *doc, @@ -156,34 +186,66 @@ dom_exception _dom_document_rename_node(struct dom_document *doc, _dom_document_get_dom_config, \ _dom_document_normalize, \ _dom_document_rename_node +/* End of vtable */ + +/* Following comes the protected vtable */ +void _dom_document_destroy(struct dom_node_internal *node); +dom_exception __dom_document_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret); +dom_exception _dom_document_copy(struct dom_node_internal *new, + struct dom_node_internal *old); -/* Initialise the document module */ -dom_exception _dom_document_initialise(dom_alloc alloc, void *pw); -/* Finalise the document module */ -dom_exception _dom_document_finalise(void); +#define DOM_DOCUMENT_PROTECT_VTABLE \ + _dom_document_destroy, \ + __dom_document_alloc, \ + _dom_document_copy -/* Destroy a document */ -void dom_document_destroy(struct dom_node_internal *dnode); +/*---------------------------- Helper functions ---------------------------*/ + +/* Try to destroy the document: + * When the refcnt is zero and the pending list is empty, we can destroy this + * document. */ +void _dom_document_try_destroy(struct dom_document *doc); /* (De)allocate memory */ -void *dom_document_alloc(struct dom_document *doc, void *ptr, size_t size); +void *_dom_document_alloc(struct dom_document *doc, void *ptr, size_t size); + +/* Get the internment context */ +inline struct lwc_context_s *_dom_document_get_intern_context( + struct dom_document *doc); + +/* Get the resource manager inside this document, a resource manager + * is an object which contain the memory allocator/intern string context, + * with which we can allocate strings or intern strings */ +void _dom_document_get_resource_mgr( + struct dom_document *doc, struct dom_resource_mgr *rm); + +/* Get the internal allocator and its pointer */ +inline void _dom_document_get_allocator(struct dom_document *doc, + dom_alloc *al, void **pw); + +/* Create a hash_table */ +dom_exception _dom_document_create_hashtable(struct dom_document *doc, + size_t chains, dom_hash_func f, struct dom_hash_table **ht); /* Get a nodelist, creating one if necessary */ -dom_exception dom_document_get_nodelist(struct dom_document *doc, - struct dom_node_internal *root, struct dom_string *tagname, - struct dom_string *namespace, struct dom_string *localname, - struct dom_nodelist **list); +dom_exception _dom_document_get_nodelist(struct dom_document *doc, + nodelist_type type, struct dom_node_internal *root, + struct lwc_string_s *tagname, struct lwc_string_s *namespace, + struct lwc_string_s *localname, struct dom_nodelist **list); /* Remove a nodelist */ -void dom_document_remove_nodelist(struct dom_document *doc, +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_internal *head, dom_node_type type, - struct dom_namednodemap **map); -/* Remove a namednodemap */ -void dom_document_remove_namednodemap(struct dom_document *doc, - struct dom_namednodemap *map); +/* Find element with certain ID in the subtree rooted at root */ +dom_exception _dom_find_element_by_id(dom_node_internal *root, + struct lwc_string_s *id, struct dom_element **result); + +/* Set the ID attribute name of this document */ +void _dom_document_set_id_name(struct dom_document *doc, + struct lwc_string_s *name); + +#define _dom_document_get_id_name(d) (d->id_name) #endif diff --git a/src/core/document_type.c b/src/core/document_type.c index fc1a1e9..fd59ef1 100644 --- a/src/core/document_type.c +++ b/src/core/document_type.c @@ -4,15 +4,20 @@ * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell * Copyright 2007 James Shaw + * Copyright 2009 Bo Yang */ +#include + #include -#include #include +#include "core/string.h" #include "core/document_type.h" #include "core/node.h" #include "utils/utils.h" +#include "utils/namespace.h" +#include "utils/resource_mgr.h" /** * DOM DocumentType node @@ -20,12 +25,12 @@ struct dom_document_type { struct dom_node_internal base; /**< Base node */ - /** \todo other members */ + struct dom_implementation *impl; /**< Owning implementation */ + struct dom_string *public_id; /**< Doctype public ID */ struct dom_string *system_id; /**< Doctype system ID */ - dom_alloc alloc; /**< Memory (de)allocation function */ - void *pw; /**< Pointer to private data */ + struct dom_resource_mgr res; /**< resource_mgr of this node */ }; static struct dom_document_type_vtable document_type_vtable = { @@ -35,6 +40,15 @@ static struct dom_document_type_vtable document_type_vtable = { DOM_DOCUMENT_TYPE_VTABLE }; +static struct dom_node_protect_vtable dt_protect_vtable = { + DOM_DT_PROTECT_VTABLE +}; + + +/*----------------------------------------------------------------------*/ + +/* Constructors and destructors */ + /** * Create a document type node * @@ -52,7 +66,8 @@ static struct dom_document_type_vtable document_type_vtable = { */ dom_exception dom_document_type_create(struct dom_string *qname, struct dom_string *public_id, struct dom_string *system_id, - dom_alloc alloc, void *pw, struct dom_document_type **doctype) + dom_alloc alloc, void *pw, struct lwc_context_s *ctx, + struct dom_document_type **doctype) { struct dom_document_type *result; dom_exception err; @@ -62,28 +77,12 @@ dom_exception dom_document_type_create(struct dom_string *qname, if (result == NULL) return DOM_NO_MEM_ERR; - /* Initialise base node */ - err = dom_node_initialise(&result->base, NULL, DOM_DOCUMENT_TYPE_NODE, - qname, NULL, NULL, NULL); - if (err != DOM_NO_ERR) { - alloc(result, 0, pw); - return err; - } - /* Initialise the vtable */ result->base.base.vtable = &document_type_vtable; - result->base.destroy = &dom_document_type_destroy; - - /* Get public and system IDs */ - dom_string_ref(public_id); - result->public_id = public_id; - - dom_string_ref(system_id); - result->system_id = system_id; - - /* Fill in allocation information */ - result->alloc = alloc; - result->pw = pw; + result->base.vtable = &dt_protect_vtable; + + err = _dom_document_type_initialise(result, qname, public_id, system_id, + alloc, pw, ctx); *doctype = result; @@ -97,22 +96,108 @@ dom_exception dom_document_type_create(struct dom_string *qname, * * The contents of ::doctype will be destroyed and ::doctype will be freed. */ -void dom_document_type_destroy(struct dom_node_internal *doctypenode) +void _dom_document_type_destroy(struct dom_node_internal *doctypenode) { struct dom_document_type *doctype = (struct dom_document_type *)doctypenode; - /* Finish with public and system IDs */ - dom_string_unref(doctype->system_id); - dom_string_unref(doctype->public_id); - /* Finalise base class */ - dom_node_finalise(doctype->base.owner, &doctype->base); + _dom_document_type_finalise(doctype); /* Free doctype */ - doctype->alloc(doctype, 0, doctype->pw); + doctype->res.alloc(doctype, 0, doctype->res.pw); } +/* Initialise this document_type */ +dom_exception _dom_document_type_initialise(struct dom_document_type *doctype, + struct dom_string *qname, struct dom_string *public_id, + struct dom_string *system_id, dom_alloc alloc, void *pw, + struct lwc_context_s *ctx) +{ + dom_exception err; + + dom_string *prefix, *localname; + err = _dom_namespace_split_qname(qname, &prefix, &localname); + if (err != DOM_NO_ERR) { + alloc(doctype, 0, pw); + return err; + } + + lwc_string *lprefix = NULL, *lname = NULL; + if (prefix != NULL) { + err = _dom_string_intern(prefix, ctx, &lprefix); + if (err != DOM_NO_ERR) { + dom_string_unref(prefix); + dom_string_unref(localname); + alloc(doctype, 0, pw); + return err; + } + } + + if (localname != NULL) { + err = _dom_string_intern(localname, ctx, &lname); + if (err != DOM_NO_ERR) { + dom_string_unref(prefix); + dom_string_unref(localname); + if (lprefix != NULL) + lwc_context_string_unref(ctx, lprefix); + alloc(doctype, 0, pw); + return err; + } + } + + /* TODO: I should figure out how the namespaceURI can be got */ + + /* Initialise base node */ + err = _dom_node_initialise_generic(&doctype->base, NULL, alloc, pw, + ctx, DOM_DOCUMENT_TYPE_NODE, lname, NULL, NULL, + lprefix); + if (err != DOM_NO_ERR) { + alloc(doctype, 0, pw); + return err; + } + + /* Get public and system IDs */ + if (public_id != NULL) + dom_string_ref(public_id); + doctype->public_id = public_id; + + if (system_id != NULL) + dom_string_ref(system_id); + doctype->system_id = system_id; + + if (prefix != NULL) + dom_string_unref(prefix); + if (localname != NULL) + dom_string_unref(localname); + + /* Fill in allocation information */ + doctype->res.alloc = alloc; + doctype->res.pw = pw; + doctype->res.ctx = ctx; + + return DOM_NO_ERR; +} + +/* The destructor function of dom_document_type */ +void _dom_document_type_finalise(struct dom_document_type *doctype) +{ + if (doctype->public_id != NULL) + dom_string_unref(doctype->public_id); + if (doctype->system_id != NULL) + dom_string_unref(doctype->system_id); + + assert(doctype->base.owner != NULL || doctype->base.user_data == NULL); + + _dom_node_finalise_generic(&doctype->base, doctype->res.alloc, + doctype->res.pw, doctype->res.ctx); +} + + +/*----------------------------------------------------------------------*/ + +/* Virtual functions */ + /** * Retrieve a document type's name * @@ -123,6 +208,9 @@ void dom_document_type_destroy(struct dom_node_internal *doctypenode) * The returned string will have its reference count increased. It is * the responsibility of the caller to unref the string once it has * finished with it. + * + * We don't support this API now, so this function call should always + * return DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_type_get_name(struct dom_document_type *doc_type, struct dom_string **result) @@ -143,6 +231,9 @@ dom_exception _dom_document_type_get_name(struct dom_document_type *doc_type, * 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. + * + * We don't support this API now, so this function call should always + * return DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_type_get_entities( struct dom_document_type *doc_type, @@ -164,6 +255,9 @@ dom_exception _dom_document_type_get_entities( * 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. + * + * We don't support this API now, so this function call should always + * return DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_type_get_notations( struct dom_document_type *doc_type, @@ -185,6 +279,9 @@ dom_exception _dom_document_type_get_notations( * The returned string will have its reference count increased. It is * the responsibility of the caller to unref the string once it has * finished with it. + * + * We don't support this API now, so this function call should always + * return DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_type_get_public_id( struct dom_document_type *doc_type, @@ -206,6 +303,9 @@ dom_exception _dom_document_type_get_public_id( * The returned string will have its reference count increased. It is * the responsibility of the caller to unref the string once it has * finished with it. + * + * We don't support this API now, so this function call should always + * return DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_type_get_system_id( struct dom_document_type *doc_type, @@ -227,6 +327,9 @@ dom_exception _dom_document_type_get_system_id( * The returned string will have its reference count increased. It is * the responsibility of the caller to unref the string once it has * finished with it. + * + * We don't support this API now, so this function call should always + * return DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_document_type_get_internal_subset( struct dom_document_type *doc_type, @@ -238,3 +341,60 @@ dom_exception _dom_document_type_get_internal_subset( return DOM_NOT_SUPPORTED_ERR; } +/*-----------------------------------------------------------------------*/ + +/* Overload protected virtual functions */ + +/* The virtual destroy function of this class */ +void _dom_dt_destroy(struct dom_node_internal *node) +{ + _dom_document_type_destroy(node); +} + +/* The memory allocator of this class */ +dom_exception _dom_dt_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + UNUSED(doc); + UNUSED(n); + UNUSED(ret); + + return DOM_NOT_SUPPORTED_ERR; +} + +/* The copy constructor of this class */ +dom_exception _dom_dt_copy(struct dom_node_internal *new, + struct dom_node_internal *old) +{ + UNUSED(new); + UNUSED(old); + + return DOM_NOT_SUPPORTED_ERR; +} + + +/*----------------------------------------------------------------------*/ + +/* Helper functions */ + +/* Get the resource manager of this object */ +void _dom_document_type_get_resource_mgr( + struct dom_document_type *dt, struct dom_resource_mgr *rm) +{ + rm->alloc = dt->res.alloc; + rm->pw = dt->res.pw; + rm->ctx = dt->res.ctx; +} + +/** + * Get the implementation which created this dom_document_type + * + * \param dt The document type object + * \return the dom_implementation instance which creates this node. + */ +struct dom_implementation *_dom_document_type_get_impl( + struct dom_document_type *dt) +{ + return dt->impl; +} + diff --git a/src/core/document_type.h b/src/core/document_type.h index e38cf52..649b027 100644 --- a/src/core/document_type.h +++ b/src/core/document_type.h @@ -9,9 +9,16 @@ #define dom_internal_core_document_type_h_ struct dom_document_type; +struct dom_resource_mgr; +struct dom_implementation; /* Destroy a document type */ -void dom_document_type_destroy(struct dom_node_internal *doctypenode); +void _dom_document_type_destroy(struct dom_node_internal *doctypenode); +dom_exception _dom_document_type_initialise(struct dom_document_type *doctype, + struct dom_string *qname, struct dom_string *public_id, + struct dom_string *system_id, dom_alloc alloc, void *pw, + struct lwc_context_s *ctx); +void _dom_document_type_finalise(struct dom_document_type *doctype); /* The virtual functions of DocumentType */ dom_exception _dom_document_type_get_name(struct dom_document_type *doc_type, @@ -40,5 +47,22 @@ dom_exception _dom_document_type_get_internal_subset( _dom_document_type_get_system_id, \ _dom_document_type_get_internal_subset -#endif +/* Following comes the protected vtable */ +void _dom_dt_destroy(struct dom_node_internal *node); +dom_exception _dom_dt_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret); +dom_exception _dom_dt_copy(struct dom_node_internal *new, + struct dom_node_internal *old); + +#define DOM_DT_PROTECT_VTABLE \ + _dom_dt_destroy, \ + _dom_dt_alloc, \ + _dom_dt_copy +/* Helper functions */ +void _dom_document_type_get_resource_mgr( + struct dom_document_type *dt, struct dom_resource_mgr *rm); +struct dom_implementation *_dom_document_type_get_impl( + struct dom_document_type *dt); + +#endif 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 + * Copyright 2009 Bo Yang */ #include +#include +#include +#include #include #include #include #include +#include #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; - } - - /* Ensure element can be written to */ - if (_dom_node_readonly(e)) { - return DOM_NO_MODIFICATION_ALLOWED_ERR; - } + if (err != DOM_NO_ERR) + return DOM_NAMESPACE_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 - * \return DOM_NO_ERR. + * \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_has_attributes(struct dom_element *element, - bool *result) +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) { - *result = (element->attributes != NULL); + 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 */ + /** - * Retrieve a pointer to the first attribute attached to an element + * The internal helper function for getAttribute/getAttributeNS. * - * \param element The element to retrieve the first attribute from - * \return Pointer to first attribute, or NULL if none. + * \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. */ -struct dom_node_internal *dom_element_get_first_attribute( - struct dom_element *element) +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) { - return (struct dom_node_internal *) element->attributes; + 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_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) +{ + 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; +} + +/** + * (Un)set an attribute Node as a ID. + * + * \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. + */ +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) +{ + 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; + } } diff --git a/src/core/element.h b/src/core/element.h index a05b8c0..f987fcd 100644 --- a/src/core/element.h +++ b/src/core/element.h @@ -19,6 +19,7 @@ struct dom_node; struct dom_string; struct dom_attr; struct dom_type_info; +struct dom_hash_table; /** * DOM element node @@ -26,33 +27,31 @@ struct dom_type_info; struct dom_element { struct dom_node_internal base; /**< Base node */ - struct dom_attr *attributes; /**< Element attributes */ + struct dom_hash_table *attributes; /**< Element attributes */ - struct dom_type_info *schema_type_info; /**< Type information */ -}; - -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); + struct dom_hash_table *ns_attributes; + /**< Attributes with prefix */ -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); + struct lwc_string_s *id_ns; /**< The id attribute's namespace */ -void dom_element_destroy(struct dom_document *doc, - struct dom_element *element); + struct lwc_string_s *id_name; /**< The id attribute's name */ -void _dom_element_destroy(struct dom_node_internal *node); + struct dom_type_info *schema_type_info; /**< Type information */ +}; -dom_exception dom_element_get_attributes(struct dom_element *element, - struct dom_namednodemap **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 dom_element_has_attributes(struct dom_element *element, - bool *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_node_internal *dom_element_get_first_attribute( +void _dom_element_destroy(struct dom_document *doc, struct dom_element *element); + /* The virtual functions of dom_element */ dom_exception _dom_element_get_tag_name(struct dom_element *element, struct dom_string **name); @@ -68,8 +67,9 @@ dom_exception _dom_element_set_attribute_node(struct dom_element *element, struct dom_attr *attr, struct dom_attr **result); dom_exception _dom_element_remove_attribute_node(struct dom_element *element, struct dom_attr *attr, struct dom_attr **result); -dom_exception _dom_element_get_elements_by_tag_name(struct dom_element *element, - struct dom_string *name, struct dom_nodelist **result); +dom_exception _dom_element_get_elements_by_tag_name( + struct dom_element *element, struct dom_string *name, + struct dom_nodelist **result); dom_exception _dom_element_get_attribute_ns(struct dom_element *element, struct dom_string *namespace, struct dom_string *localname, struct dom_string **value); @@ -123,4 +123,72 @@ dom_exception _dom_element_set_id_attribute_node(struct dom_element *element, _dom_element_set_id_attribute_ns, \ _dom_element_set_id_attribute_node +/* Overloading dom_node functions */ +dom_exception _dom_element_get_attributes(dom_node_internal *node, + struct dom_namednodemap **result); +dom_exception _dom_element_has_attributes(dom_node_internal *node, + bool *result); +dom_exception _dom_element_normalize(dom_node_internal *node); +dom_exception _dom_element_lookup_prefix(dom_node_internal *node, + struct dom_string *namespace, struct dom_string **result); +dom_exception _dom_element_is_default_namespace(dom_node_internal *node, + struct dom_string *namespace, bool *result); +dom_exception _dom_element_lookup_namespace(dom_node_internal *node, + struct dom_string *prefix, struct dom_string **result); +#define DOM_NODE_VTABLE_ELEMENT \ + _dom_node_get_node_name, \ + _dom_node_get_node_value, \ + _dom_node_set_node_value, \ + _dom_node_get_node_type, \ + _dom_node_get_parent_node, \ + _dom_node_get_child_nodes, \ + _dom_node_get_first_child, \ + _dom_node_get_last_child, \ + _dom_node_get_previous_sibling, \ + _dom_node_get_next_sibling, \ + _dom_element_get_attributes, /*overload*/\ + _dom_node_get_owner_document, \ + _dom_node_insert_before, \ + _dom_node_replace_child, \ + _dom_node_remove_child, \ + _dom_node_append_child, \ + _dom_node_has_child_nodes, \ + _dom_node_clone_node, \ + _dom_node_normalize, \ + _dom_node_is_supported, \ + _dom_node_get_namespace, \ + _dom_node_get_prefix, \ + _dom_node_set_prefix, \ + _dom_node_get_local_name, \ + _dom_element_has_attributes, /*overload*/\ + _dom_node_get_base, \ + _dom_node_compare_document_position, \ + _dom_node_get_text_content, \ + _dom_node_set_text_content, \ + _dom_node_is_same, \ + _dom_element_lookup_prefix, /*overload*/\ + _dom_element_is_default_namespace, /*overload*/\ + _dom_element_lookup_namespace, /*overload*/\ + _dom_node_is_equal, \ + _dom_node_get_feature, \ + _dom_node_set_user_data, \ + _dom_node_get_user_data + + +/* The protected virtual function */ +void __dom_element_destroy(dom_node_internal *node); +dom_exception _dom_element_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret); +dom_exception _dom_element_copy(struct dom_node_internal *new, + struct dom_node_internal *old); + +#define DOM_ELEMENT_PROTECT_VTABLE \ + __dom_element_destroy, \ + _dom_element_alloc, \ + _dom_element_copy + +/* Helper functions*/ +dom_exception _dom_element_get_id(struct dom_element *ele, + struct lwc_string_s **id); + #endif diff --git a/src/core/entity_ref.c b/src/core/entity_ref.c index 2b90c79..c5b426c 100644 --- a/src/core/entity_ref.c +++ b/src/core/entity_ref.c @@ -3,8 +3,11 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ +#include + #include "core/document.h" #include "core/entity_ref.h" #include "core/node.h" @@ -17,6 +20,14 @@ struct dom_entity_reference { struct dom_node_internal base; /**< Base node */ }; +static struct dom_node_vtable er_vtable = { + DOM_NODE_VTABLE +}; + +static struct dom_node_protect_vtable er_protect_vtable = { + DOM_ER_PROTECT_VTABLE +}; + /** * Create an entity reference * @@ -31,24 +42,27 @@ struct dom_entity_reference { * * The returned node will already be referenced. */ -dom_exception dom_entity_reference_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *value, +dom_exception _dom_entity_reference_create(struct dom_document *doc, + struct lwc_string_s *name, struct dom_string *value, struct dom_entity_reference **result) { struct dom_entity_reference *e; dom_exception err; /* Allocate the comment node */ - e = dom_document_alloc(doc, NULL, + e = _dom_document_alloc(doc, NULL, sizeof(struct dom_entity_reference)); if (e == NULL) return DOM_NO_MEM_ERR; + e->base.base.vtable = &er_vtable; + e->base.vtable = &er_protect_vtable; + /* And initialise the node */ - err = dom_node_initialise(&e->base, doc, DOM_ENTITY_REFERENCE_NODE, - name, value, NULL, NULL); + err = _dom_entity_reference_initialise(&e->base, doc, + DOM_ENTITY_REFERENCE_NODE, name, value, NULL, NULL); if (err != DOM_NO_ERR) { - dom_document_alloc(doc, e, 0); + _dom_document_alloc(doc, e, 0); return err; } @@ -65,52 +79,28 @@ dom_exception dom_entity_reference_create(struct dom_document *doc, * * The contents of ::entity will be destroyed and ::entity will be freed. */ -void dom_entity_reference_destroy(struct dom_document *doc, +void _dom_entity_reference_destroy(struct dom_document *doc, struct dom_entity_reference *entity) { - struct dom_node_internal *c, *d; - - /* Destroy children of this node */ - for (c = entity->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); - } - /* Finalise base class */ - dom_node_finalise(doc, &entity->base); + _dom_entity_reference_finalise(doc, &entity->base); /* Destroy fragment */ - dom_document_alloc(doc, entity, 0); + _dom_document_alloc(doc, entity, 0); } /** - * Get the textual representation of an EntityReference + * Get the textual representation of an EntityRererence * * \param entity The entity reference to get the textual representation of * \param result Pointer to location to receive result * \return DOM_NO_ERR on success. * * The returned string will have its reference count increased. It is - * the responsibility of the caller to unref the string once it has + * the responsibility of the caller to unrer the string once it has * finished with it. */ -dom_exception dom_entity_reference_get_textual_representation( +dom_exception _dom_entity_reference_get_textual_representation( struct dom_entity_reference *entity, struct dom_string **result) { UNUSED(entity); @@ -119,3 +109,39 @@ dom_exception dom_entity_reference_get_textual_representation( return DOM_NOT_SUPPORTED_ERR; } +/*-----------------------------------------------------------------------*/ + +/* Following comes the protected vtable */ + +/* The virtual destroy function of this class */ +void _dom_er_destroy(struct dom_node_internal *node) +{ + _dom_entity_reference_destroy(node->owner, + (struct dom_entity_reference *) node); +} + +/* The memory allocator of this class */ +dom_exception _dom_er_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + UNUSED(n); + dom_entity_reference *a; + + a = _dom_document_alloc(doc, NULL, sizeof(struct dom_entity_reference)); + if (a == NULL) + return DOM_NO_MEM_ERR; + + *ret = (dom_node_internal *) a; + dom_node_set_owner(*ret, doc); + + return DOM_NO_ERR; + +} + +/* The copy constructor of this class */ +dom_exception _dom_er_copy(struct dom_node_internal *new, + struct dom_node_internal *old) +{ + return _dom_node_copy(new, old); +} + diff --git a/src/core/entity_ref.h b/src/core/entity_ref.h index fa03737..2b83d07 100644 --- a/src/core/entity_ref.h +++ b/src/core/entity_ref.h @@ -5,23 +5,42 @@ * Copyright 2007 John-Mark Bell */ -#ifndef dom_internal_core_entityreference_h_ -#define dom_internal_core_entityreference_h_ +#ifndef dom_internal_core_entityrererence_h_ +#define dom_internal_core_entityrererence_h_ #include +#include struct dom_document; struct dom_entity_reference; struct dom_string; +struct lwc_string_s; -dom_exception dom_entity_reference_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *value, +dom_exception _dom_entity_reference_create(struct dom_document *doc, + struct lwc_string_s *name, struct dom_string *value, struct dom_entity_reference **result); -void dom_entity_reference_destroy(struct dom_document *doc, +void _dom_entity_reference_destroy(struct dom_document *doc, struct dom_entity_reference *entity); -dom_exception dom_entity_reference_get_textual_representation( +#define _dom_entity_reference_initialise _dom_node_initialise +#define _dom_entity_reference_finalise _dom_node_finalise + +/* Following comes the protected vtable */ +void _dom_er_destroy(struct dom_node_internal *node); +dom_exception _dom_er_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret); +dom_exception _dom_er_copy(struct dom_node_internal *new, + struct dom_node_internal *old); + +#define DOM_ER_PROTECT_VTABLE \ + _dom_er_destroy, \ + _dom_er_alloc, \ + _dom_er_copy + +/* Helper functions */ +dom_exception _dom_entity_reference_get_textual_representation( struct dom_entity_reference *entity, struct dom_string **result); + #endif diff --git a/src/core/implementation.c b/src/core/implementation.c index e37b27d..e2b6763 100644 --- a/src/core/implementation.c +++ b/src/core/implementation.c @@ -59,8 +59,6 @@ dom_exception dom_implementation_has_feature( * \param public_id The external subset public identifier * \param system_id The external subset system identifier * \param doctype Pointer to location to receive result - * \param alloc Memory (de)allocation function - * \param pw Pointer to client-specific private data * \return DOM_NO_ERR on success, * DOM_INVALID_CHARACTER_ERR if ::qname is invalid, * DOM_NAMESPACE_ERR if ::qname is malformed, @@ -79,11 +77,11 @@ dom_exception dom_implementation_has_feature( dom_exception dom_implementation_create_document_type( struct dom_implementation *impl, struct dom_string *qname, struct dom_string *public_id, struct dom_string *system_id, - struct dom_document_type **doctype, - dom_alloc alloc, void *pw) + dom_alloc alloc, void *pw, struct lwc_context_s *ctx, + struct dom_document_type **doctype) { return impl->create_document_type(impl, qname, public_id, system_id, - doctype, alloc, pw); + alloc, pw, ctx, doctype); } /** @@ -94,8 +92,6 @@ dom_exception dom_implementation_create_document_type( * \param qname The qualified name of the document element * \param doctype The type of document to create * \param doc Pointer to location to receive result - * \param alloc Memory (de)allocation function - * \param pw Pointer to client-specific private data * \return DOM_NO_ERR on success, * DOM_INVALID_CHARACTER_ERR if ::qname is invalid, * DOM_NAMESPACE_ERR if ::qname is malformed, or if ::qname @@ -125,11 +121,11 @@ dom_exception dom_implementation_create_document( struct dom_implementation *impl, struct dom_string *namespace, struct dom_string *qname, struct dom_document_type *doctype, - struct dom_document **doc, - dom_alloc alloc, void *pw) + dom_alloc alloc, void *pw, struct lwc_context_s *ctx, + struct dom_document **doc) { - return impl->create_document(impl, namespace, qname, doctype, doc, - alloc, pw); + return impl->create_document(impl, namespace, qname, doctype, alloc, + pw, ctx, doc); } /** @@ -140,8 +136,6 @@ dom_exception dom_implementation_create_document( * \param feature The requested feature * \param version The version number of the feature * \param object Pointer to location to receive object - * \param alloc Memory (de)allocation function - * \param pw Pointer to client-specific private data * \return DOM_NO_ERR. * * Any memory allocated by this call should be allocated using @@ -150,8 +144,7 @@ dom_exception dom_implementation_create_document( dom_exception dom_implementation_get_feature( struct dom_implementation *impl, struct dom_string *feature, struct dom_string *version, - void **object, - dom_alloc alloc, void *pw) + void **object) { - return impl->get_feature(impl, feature, version, object, alloc, pw); + return impl->get_feature(impl, feature, version, object); } diff --git a/src/core/impllist.c b/src/core/impllist.c index 522c3f7..2f25926 100644 --- a/src/core/impllist.c +++ b/src/core/impllist.c @@ -9,6 +9,9 @@ #include #include +extern void dom_implementation_list_destroy( + struct dom_implementation_list *list); + /** * Claim a reference on a DOM implementation list * @@ -29,22 +32,8 @@ void dom_implementation_list_ref(struct dom_implementation_list *list) */ void dom_implementation_list_unref(struct dom_implementation_list *list) { - struct dom_implementation_list_item *i, *j; - if (--list->refcnt == 0) { - /* Destroy all list entries */ - for (i = list->head; i; i = j) { - j = i->next; - - /* Unreference the implementation */ - dom_implementation_unref(i->impl); - - /* And free the entry */ - list->alloc(i, 0, list->pw); - } - - /* Free the list object */ - list->alloc(list, 0, list->pw); + dom_implementation_list_destroy(list); } } diff --git a/src/core/namednodemap.c b/src/core/namednodemap.c index dab6bbb..ec1e151 100644 --- a/src/core/namednodemap.c +++ b/src/core/namednodemap.c @@ -3,8 +3,11 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ +#include + #include #include #include @@ -22,9 +25,10 @@ struct dom_namednodemap { struct dom_document *owner; /**< Owning document */ - struct dom_node_internal *head; /**< Start of item list */ + void *priv; /**< Private data */ - dom_node_type type; /**< Type of items in map */ + struct nnm_operation *opt; /**< The underlaid operation + * implementations */ uint32_t refcnt; /**< Reference count */ }; @@ -33,8 +37,8 @@ struct dom_namednodemap { * Create a namednodemap * * \param doc The owning document - * \param head Start of list containing items in map - * \param type The type of items in the map + * \param priv The private data of this dom_namednodemap + * \param opt The operation function pointer * \param map Pointer to location to receive created map * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion * @@ -49,23 +53,20 @@ struct dom_namednodemap { * 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_internal *head, dom_node_type type, +dom_exception _dom_namednodemap_create(struct dom_document *doc, + void *priv, struct nnm_operation *opt, struct dom_namednodemap **map) { struct dom_namednodemap *m; - m = dom_document_alloc(doc, NULL, sizeof(struct dom_namednodemap)); + 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(head); - m->head = head; - - m->type = type; + m->priv = priv; + m->opt = opt; m->refcnt = 1; @@ -81,6 +82,7 @@ dom_exception dom_namednodemap_create(struct dom_document *doc, */ void dom_namednodemap_ref(struct dom_namednodemap *map) { + assert(map != NULL); map->refcnt++; } @@ -94,22 +96,15 @@ void dom_namednodemap_ref(struct dom_namednodemap *map) */ void dom_namednodemap_unref(struct dom_namednodemap *map) { - if (--map->refcnt == 0) { - struct dom_node_internal *owner = - (struct dom_node_internal *) map->owner; + if (map == NULL) + return; - dom_node_unref(map->head); - - /* Remove map from document */ - dom_document_remove_namednodemap(map->owner, map); + if (--map->refcnt == 0) { + /* Call the implementation specific destroy */ + map->opt->namednodemap_destroy(map->priv); /* 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); + _dom_document_alloc(map->owner, map, 0); } } @@ -123,29 +118,8 @@ void dom_namednodemap_unref(struct dom_namednodemap *map) dom_exception dom_namednodemap_get_length(struct dom_namednodemap *map, unsigned long *length) { - struct dom_node_internal *cur; - unsigned long len = 0; - - switch (map->type) { - case DOM_ATTRIBUTE_NODE: - cur = dom_element_get_first_attribute( - (struct dom_element *) map->head); - break; - case DOM_NOTATION_NODE: - case DOM_ENTITY_NODE: - /** \todo handle notation and entity nodes */ - default: - return DOM_NOT_SUPPORTED_ERR; - break; - } - - for (; cur != NULL; cur = cur->next) { - len++; - } - - *length = len; - - return DOM_NO_ERR; + assert(map->opt != NULL); + return map->opt->namednodemap_get_length(map->priv, length); } /** @@ -162,33 +136,8 @@ dom_exception dom_namednodemap_get_length(struct dom_namednodemap *map, dom_exception _dom_namednodemap_get_named_item(struct dom_namednodemap *map, struct dom_string *name, struct dom_node **node) { - struct dom_node_internal *cur; - - switch (map->type) { - case DOM_ATTRIBUTE_NODE: - cur = dom_element_get_first_attribute( - (struct dom_element *) map->head); - break; - case DOM_NOTATION_NODE: - case DOM_ENTITY_NODE: - /** \todo handle notation and entity nodes */ - default: - return DOM_NOT_SUPPORTED_ERR; - break; - } - - for (; cur != NULL; cur = cur->next) { - if (dom_string_cmp(cur->name, name) == 0) { - break; - } - } - - if (cur != NULL) { - dom_node_ref(cur); - } - *node = (struct dom_node *) cur; - - return DOM_NO_ERR; + assert(map->opt != NULL); + return map->opt->namednodemap_get_named_item(map->priv, name, node); } /** @@ -218,46 +167,8 @@ dom_exception _dom_namednodemap_get_named_item(struct dom_namednodemap *map, dom_exception _dom_namednodemap_set_named_item(struct dom_namednodemap *map, struct dom_node *arg, struct dom_node **node) { - dom_exception err; - struct dom_node_internal *n = (struct dom_node_internal *) arg; - - /* Ensure arg and map belong to the same document */ - if (n->owner != map->owner) - return DOM_WRONG_DOCUMENT_ERR; - - /* Ensure map is writable */ - if (_dom_node_readonly(map->head)) - return DOM_NO_MODIFICATION_ALLOWED_ERR; - - /* Ensure arg isn't attached to another element */ - if (n->type == DOM_ATTRIBUTE_NODE && n->parent != NULL && - n->parent != map->head) - return DOM_INUSE_ATTRIBUTE_ERR; - - /* Ensure arg is permitted in the map */ - if (n->type != map->type) - return DOM_HIERARCHY_REQUEST_ERR; - - /* Now delegate to the container-specific function. - * NamedNodeMaps are live, so this is fine. */ - switch (map->type) { - case DOM_ATTRIBUTE_NODE: - err = dom_element_set_attribute_node( - (struct dom_element *) map->head, - (struct dom_attr *) arg, - (struct dom_attr **) node); - break; - case DOM_NOTATION_NODE: - case DOM_ENTITY_NODE: - /** \todo handle notation and entity nodes */ - default: - err = DOM_NOT_SUPPORTED_ERR; - break; - } - - /* Reference counting is handled by the container-specific call */ - - return err; + assert(map->opt != NULL); + return map->opt->namednodemap_set_named_item(map->priv, arg, node); } /** @@ -278,44 +189,8 @@ dom_exception _dom_namednodemap_remove_named_item( struct dom_namednodemap *map, struct dom_string *name, struct dom_node **node) { - dom_exception err; - - /* Ensure map is writable */ - if (_dom_node_readonly(map->head)) - return DOM_NO_MODIFICATION_ALLOWED_ERR; - - /* Now delegate to the container-specific function. - * NamedNodeMaps are live, so this is fine. */ - switch (map->type) { - case DOM_ATTRIBUTE_NODE: - { - struct dom_attr *attr; - - err = dom_element_get_attribute_node( - (struct dom_element *) map->head, - name, &attr); - if (err == DOM_NO_ERR) { - err = dom_element_remove_attribute_node( - (struct dom_element *) map->head, - attr, (struct dom_attr **) node); - if (err == DOM_NO_ERR) { - /* No longer want attr */ - dom_node_unref((struct dom_node *) attr); - } - } - } - break; - case DOM_NOTATION_NODE: - case DOM_ENTITY_NODE: - /** \todo handle notation and entity nodes */ - default: - err = DOM_NOT_SUPPORTED_ERR; - break; - } - - /* Reference counting is handled by the container-specific call */ - - return err; + assert(map->opt != NULL); + return map->opt->namednodemap_remove_named_item(map->priv, name, node); } /** @@ -335,36 +210,8 @@ dom_exception _dom_namednodemap_remove_named_item( dom_exception _dom_namednodemap_item(struct dom_namednodemap *map, unsigned long index, struct dom_node **node) { - struct dom_node_internal *cur; - unsigned long count = 0; - - switch (map->type) { - case DOM_ATTRIBUTE_NODE: - cur = dom_element_get_first_attribute( - (struct dom_element *) map->head); - break; - case DOM_NOTATION_NODE: - case DOM_ENTITY_NODE: - /** \todo handle notation and entity nodes */ - default: - return DOM_NOT_SUPPORTED_ERR; - break; - } - - for (; cur != NULL; cur = cur->next) { - count++; - - if ((index + 1) == count) { - break; - } - } - - if (cur != NULL) { - dom_node_ref(cur); - } - *node = (struct dom_node *) cur; - - return DOM_NO_ERR; + assert(map->opt != NULL); + return map->opt->namednodemap_item(map->priv, index, node); } /** @@ -387,38 +234,9 @@ dom_exception _dom_namednodemap_get_named_item_ns( struct dom_namednodemap *map, struct dom_string *namespace, struct dom_string *localname, struct dom_node **node) { - struct dom_node_internal *cur; - - /** \todo ensure XML feature is supported */ - - switch (map->type) { - case DOM_ATTRIBUTE_NODE: - cur = dom_element_get_first_attribute( - (struct dom_element *) map->head); - break; - case DOM_NOTATION_NODE: - case DOM_ENTITY_NODE: - /** \todo handle notation and entity nodes */ - default: - return DOM_NOT_SUPPORTED_ERR; - break; - } - - for (; cur != NULL; cur = cur->next) { - if (((namespace == NULL && cur->namespace == NULL) || - (namespace != NULL && - dom_string_cmp(cur->namespace, namespace) == 0)) && - dom_string_cmp(cur->name, localname) == 0) { - break; - } - } - - if (cur != NULL) { - dom_node_ref(cur); - } - *node = (struct dom_node *) cur; - - return DOM_NO_ERR; + assert(map->opt != NULL); + return map->opt->namednodemap_get_named_item_ns(map->priv, namespace, + localname, node); } /** @@ -454,48 +272,8 @@ dom_exception _dom_namednodemap_set_named_item_ns( struct dom_namednodemap *map, struct dom_node *arg, struct dom_node **node) { - dom_exception err; - struct dom_node_internal *n = (struct dom_node_internal *) arg; - - /** \todo ensure XML feature is supported */ - - /* Ensure arg and map belong to the same document */ - if (n->owner != map->owner) - return DOM_WRONG_DOCUMENT_ERR; - - /* Ensure map is writable */ - if (_dom_node_readonly(map->head)) - return DOM_NO_MODIFICATION_ALLOWED_ERR; - - /* Ensure arg isn't attached to another element */ - if (n->type == DOM_ATTRIBUTE_NODE && n->parent != NULL && - n->parent != map->head) - return DOM_INUSE_ATTRIBUTE_ERR; - - /* Ensure arg is permitted in the map */ - if (n->type != map->type) - return DOM_HIERARCHY_REQUEST_ERR; - - /* Now delegate to the container-specific function. - * NamedNodeMaps are live, so this is fine. */ - switch (map->type) { - case DOM_ATTRIBUTE_NODE: - err = dom_element_set_attribute_node_ns( - (struct dom_element *) map->head, - (struct dom_attr *) arg, - (struct dom_attr **) node); - break; - case DOM_NOTATION_NODE: - case DOM_ENTITY_NODE: - /** \todo handle notation and entity nodes */ - default: - err = DOM_NOT_SUPPORTED_ERR; - break; - } - - /* Reference counting is handled by the container-specific call */ - - return err; + assert(map->opt != NULL); + return map->opt->namednodemap_set_named_item_ns(map->priv, arg, node); } /** @@ -521,61 +299,30 @@ dom_exception _dom_namednodemap_remove_named_item_ns( struct dom_namednodemap *map, struct dom_string *namespace, struct dom_string *localname, struct dom_node **node) { - dom_exception err; - - /** \todo ensure XML feature is supported */ - - /* Ensure map is writable */ - if (_dom_node_readonly(map->head)) - return DOM_NO_MODIFICATION_ALLOWED_ERR; - - /* Now delegate to the container-specific function. - * NamedNodeMaps are live, so this is fine. */ - switch (map->type) { - case DOM_ATTRIBUTE_NODE: - { - struct dom_attr *attr; - - err = dom_element_get_attribute_node_ns( - (struct dom_element *) map->head, - namespace, localname, &attr); - if (err == DOM_NO_ERR) { - err = dom_element_remove_attribute_node( - (struct dom_element *) map->head, - attr, (struct dom_attr **) node); - if (err == DOM_NO_ERR) { - /* No longer want attr */ - dom_node_unref((struct dom_node *) attr); - } - } - } - break; -case DOM_NOTATION_NODE: - case DOM_ENTITY_NODE: - /** \todo handle notation and entity nodes */ - default: - err = DOM_NOT_SUPPORTED_ERR; - break; - } - - /* Reference counting is handled by the container-specific call */ - - return err; + assert(map->opt != NULL); + return map->opt->namednodemap_remove_named_item_ns(map->priv, namespace, + localname, node); } /** - * Match a namednodemap instance against a set of creation parameters + * Compare whether two NamedNodeMap are equal. * - * \param map The map to match - * \param head Start of list containing items in map - * \param type The type of items in the map - * \return true if list matches, false otherwise */ -bool dom_namednodemap_match(struct dom_namednodemap *map, - struct dom_node_internal *head, dom_node_type type) +bool _dom_namednodemap_equal(struct dom_namednodemap *m1, + struct dom_namednodemap *m2) { - if (map->head == head && map->type == type) - return true; + assert(m1->opt != NULL); + return (m1->opt == m2->opt && m1->opt->namednodemap_equal(m1->priv, + m2->priv)); +} - return false; +/** + * Update the dom_namednodemap to make it as a proxy of another object + * + * \param map The dom_namednodemap + * \param priv The private data to change to + */ +void _dom_namednodemap_update(struct dom_namednodemap *map, void *priv) +{ + map->priv = priv; } diff --git a/src/core/namednodemap.h b/src/core/namednodemap.h index 830ab6d..328e433 100644 --- a/src/core/namednodemap.h +++ b/src/core/namednodemap.h @@ -18,14 +18,54 @@ struct dom_node; struct dom_namednodemap; struct dom_string; +struct nnm_operation { + dom_exception (*namednodemap_get_length)(void *priv, + unsigned long *length); + + dom_exception (*namednodemap_get_named_item)(void *priv, + struct dom_string *name, struct dom_node **node); + + dom_exception (*namednodemap_set_named_item)(void *priv, + struct dom_node *arg, struct dom_node **node); + + dom_exception (*namednodemap_remove_named_item)( + void *priv, struct dom_string *name, + struct dom_node **node); + + dom_exception (*namednodemap_item)(void *priv, + unsigned long index, struct dom_node **node); + + dom_exception (*namednodemap_get_named_item_ns)( + void *priv, struct dom_string *namespace, + struct dom_string *localname, struct dom_node **node); + + dom_exception (*namednodemap_set_named_item_ns)( + void *priv, struct dom_node *arg, + struct dom_node **node); + + dom_exception (*namednodemap_remove_named_item_ns)( + void *priv, struct dom_string *namespace, + struct dom_string *localname, struct dom_node **node); + + void (*namednodemap_destroy)(void *priv); + + bool (*namednodemap_equal)(void *p1, void *p2); +}; + /* Create a namednodemap */ -dom_exception dom_namednodemap_create(struct dom_document *doc, - struct dom_node_internal *head, dom_node_type type, +dom_exception _dom_namednodemap_create(struct dom_document *doc, + void *priv, struct nnm_operation *opt, struct dom_namednodemap **map); +/* Update the private data */ +void _dom_namednodemap_update(struct dom_namednodemap *map, void *priv); + +/* Test whether two maps are equal */ +bool _dom_namednodemap_equal(struct dom_namednodemap *m1, + struct dom_namednodemap *m2); -/* Match a namednodemap instance against a set of creation parameters */ -bool dom_namednodemap_match(struct dom_namednodemap *map, - struct dom_node_internal *head, dom_node_type type); +#define dom_namednodemap_equal(m1, m2) _dom_namednodemap_equal( \ + (struct dom_namednodemap *) (m1), \ + (struct dom_namednodemap *) (m2)) #endif diff --git a/src/core/node.c b/src/core/node.c index 518d88c..0645769 100644 --- a/src/core/node.c +++ b/src/core/node.c @@ -3,6 +3,7 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ #include @@ -10,9 +11,15 @@ #include #include +#include #include -#include +#include +#include +#include +#include +#include "core/string.h" +#include "core/namednodemap.h" #include "core/attr.h" #include "core/cdatasection.h" #include "core/comment.h" @@ -25,6 +32,7 @@ #include "core/pi.h" #include "core/text.h" #include "utils/utils.h" +#include "utils/resource_mgr.h" static bool _dom_node_permitted_child(const dom_node_internal *parent, const dom_node_internal *child); @@ -48,21 +56,27 @@ static struct dom_node_vtable node_vtable = { DOM_NODE_VTABLE }; -/** - * Create a DOM node and compose the vtable - * - * Return The new constructed DOM node or NULL if fail. - */ -dom_node_internal * dom_node_create(struct dom_document *doc) +static struct dom_node_protect_vtable node_protect_vtable = { + DOM_NODE_PROTECT_VTABLE +}; + + + +/*----------------------------------------------------------------------*/ + +/* The constructor and destructor of this object */ + +/* Create a DOM node and compose the vtable */ +dom_node_internal * _dom_node_create(struct dom_document *doc) { - dom_node_internal *node = dom_document_alloc(doc, NULL, + dom_node_internal *node = _dom_document_alloc(doc, NULL, sizeof(struct dom_node_internal)); if (node == NULL) return NULL; node->base.vtable = &node_vtable; - node->destroy = _dom_node_destroy; + node->vtable = &node_protect_vtable; return node; } @@ -86,9 +100,6 @@ void _dom_node_destroy(struct dom_node_internal *node) bool null_owner_permitted = (node->type == DOM_DOCUMENT_NODE || node->type == DOM_DOCUMENT_TYPE_NODE); - /* This function simply acts as a central despatcher - * for type-specific destructors. */ - assert(null_owner_permitted || owner != NULL); if (!null_owner_permitted) { @@ -98,56 +109,19 @@ void _dom_node_destroy(struct dom_node_internal *node) dom_node_ref(owner); } -/* This type dependent switch is not necessary from now. - But I still keep them for a while untill all the functions works well. - switch (node->type) { - case DOM_ELEMENT_NODE: - dom_element_destroy(owner, (struct dom_element *) node); - break; - case DOM_ATTRIBUTE_NODE: - dom_attr_destroy(owner, (struct dom_attr *) node); - break; - case DOM_TEXT_NODE: - dom_text_destroy(owner, (struct dom_text *) node); - break; - case DOM_CDATA_SECTION_NODE: - dom_cdata_section_destroy(owner, - (struct dom_cdata_section *) node); - break; - case DOM_ENTITY_REFERENCE_NODE: - dom_entity_reference_destroy(owner, - (struct dom_entity_reference *) node); - break; - case DOM_ENTITY_NODE: - break; - case DOM_PROCESSING_INSTRUCTION_NODE: - dom_processing_instruction_destroy(owner, - (struct dom_processing_instruction *) node); - break; - case DOM_COMMENT_NODE: - dom_comment_destroy(owner, (struct dom_comment *) node); - break; - case DOM_DOCUMENT_NODE: - dom_document_destroy((struct dom_document *) node); - break; - case DOM_DOCUMENT_TYPE_NODE: - dom_document_type_destroy((struct dom_document_type *) node); - break; - case DOM_DOCUMENT_FRAGMENT_NODE: - dom_document_fragment_destroy(owner, - (struct dom_document_fragment *) node); - break; - case DOM_NOTATION_NODE: - break; - } -*/ + /* Finalise this node, this should also destroy all the child nodes. */ + _dom_node_finalise(owner, node); + if (!null_owner_permitted) { - /* Release the reference we claimed on the document. If this - * is the last reference held on the document and the list - * of nodes pending deletion is empty, then the document will + /* Release the reference we claimed on the document. If this + * is the last reference held on the document and the list + * of nodes pending deletion is empty, then the document will * be destroyed. */ dom_node_unref(owner); } + + /* Release our memory */ + _dom_document_alloc(owner, node, 0); } /** @@ -165,13 +139,63 @@ void _dom_node_destroy(struct dom_node_internal *node) * ::name, ::value, ::namespace, and ::prefix will have their reference * counts increased. */ -dom_exception dom_node_initialise(dom_node_internal *node, +dom_exception _dom_node_initialise(dom_node_internal *node, struct dom_document *doc, dom_node_type type, - struct dom_string *name, struct dom_string *value, - struct dom_string *namespace, struct dom_string *prefix) + struct lwc_string_s *name, struct dom_string *value, + struct lwc_string_s *namespace, struct lwc_string_s *prefix) +{ + lwc_context *ctx; + dom_alloc alloc; + void *pw; + dom_exception err; + + ctx = _dom_document_get_intern_context(doc); + /* The lwc_context for a document never can be NULL */ + assert(ctx != NULL); + + _dom_document_get_allocator(doc, &alloc, &pw); + + err = _dom_node_initialise_generic(node, doc, alloc, pw, ctx, type, + name, value, namespace, prefix); + if (err != DOM_NO_ERR) + return err; + + return DOM_NO_ERR; +} + +/** + * Initialise a DOM node + * + * \param node The node to initialise + * \param doc The document object + * \param alloc The memory allocator + * \param pw The allocator private pointer data + * \param ctx The intern context + * \param type The node type required + * \param name The node (local) name, or NULL + * \param value The node value, or NULL + * \param namespace Namespace URI to use for node, or NULL + * \param prefix Namespace prefix to use for node, or NULL + * \return DOM_NO_ERR on success. + * + * ::name, ::value, ::namespace, and ::prefix will have their reference + * counts increased. + */ +dom_exception _dom_node_initialise_generic( + struct dom_node_internal *node, struct dom_document *doc, + dom_alloc alloc, void *pw, struct lwc_context_s *ctx, + dom_node_type type, struct lwc_string_s *name, + struct dom_string *value, struct lwc_string_s *namespace, + struct lwc_string_s *prefix) { + UNUSED(alloc); + UNUSED(pw); + + assert(ctx != NULL); + node->owner = doc; + if (name != NULL) - dom_string_ref(name); + lwc_context_string_ref(ctx, name); node->name = name; if (value != NULL) @@ -204,15 +228,14 @@ dom_exception dom_node_initialise(dom_node_internal *node, * deletion. This list will not be forcibly emptied, as it contains * those nodes (and their sub-trees) in use by client code. */ - node->owner = doc; if (namespace != NULL) { - dom_string_ref(namespace); + lwc_context_string_ref(ctx, namespace); } node->namespace = namespace; if (prefix != NULL) { - dom_string_ref(prefix); + lwc_context_string_ref(ctx, prefix); } node->prefix = prefix; @@ -220,6 +243,12 @@ dom_exception dom_node_initialise(dom_node_internal *node, node->refcnt = 1; + list_init(&node->pending_list); + if (node->type != DOM_DOCUMENT_NODE) { + /* A Node should be in the pending list when it is created */ + dom_node_mark_pending(node); + } + return DOM_NO_ERR; } @@ -232,33 +261,62 @@ dom_exception dom_node_initialise(dom_node_internal *node, * The contents of ::node will be cleaned up. ::node will not be freed. * All children of ::node should have been removed prior to finalisation. */ -void dom_node_finalise(struct dom_document *doc, dom_node_internal *node) +void _dom_node_finalise(struct dom_document *doc, dom_node_internal *node) +{ + lwc_context *ctx; + dom_alloc alloc; + void *pw; + + ctx = _dom_document_get_intern_context(doc); + /* The lwc_context for a document never can be NULL */ + assert(ctx != NULL); + + _dom_document_get_allocator(doc, &alloc, &pw); + + _dom_node_finalise_generic(node, alloc, pw, ctx); +} + +/** + * Finalise a DOM node + * + * \param node The node to finalise + * \param alloc The allocator + * \param pw The allocator private data + * \param ctx The intern string context + */ +void _dom_node_finalise_generic(dom_node_internal *node, dom_alloc alloc, + void *pw, struct lwc_context_s *ctx) { struct dom_user_data *u, *v; - /* Standalone DocumentType nodes may not have user data attached */ - assert(node->type != DOM_DOCUMENT_TYPE_NODE || - node->user_data == NULL); + UNUSED(alloc); + UNUSED(pw); + + assert(ctx != NULL); /* Destroy user data */ for (u = node->user_data; u != NULL; u = v) { v = u->next; - dom_string_unref(u->key); - - dom_document_alloc(doc, u, 0); + alloc(u, 0, pw); } + node->user_data = NULL; if (node->prefix != NULL) - dom_string_unref(node->prefix); + lwc_context_string_unref(ctx, node->prefix); if (node->namespace != NULL) - dom_string_unref(node->namespace); - - /** \todo check if this node is in list of nodes pending deletion. - * If so, it must be removed from the list, so the document gets - * destroyed once the list is empty (and no longer referenced) */ - node->owner = NULL; + lwc_context_string_unref(ctx, node->namespace); + + /* Destroy all the child nodes of this node */ + struct dom_node_internal *p = node->first_child; + struct dom_node_internal *n = NULL; + while (p != NULL) { + n = p->next; + p->parent = NULL; + dom_node_try_destroy(p); + p = n; + } /* Paranoia */ node->next = NULL; @@ -271,7 +329,19 @@ void dom_node_finalise(struct dom_document *doc, dom_node_internal *node) dom_string_unref(node->value); if (node->name != NULL) - dom_string_unref(node->name); + lwc_context_string_unref(ctx, node->name); + + /* Detach from the pending list, if we are in it */ + if (node->pending_list.prev != &node->pending_list) { + assert (node->pending_list.next != &node->pending_list); + list_del(&node->pending_list); + if (node->owner != NULL && node->type != DOM_DOCUMENT_NODE) { + /* Deleting this node from the pending list may cause + * the list to be null and we should try to destroy + * the document. */ + _dom_document_try_destroy(node->owner); + } + } } /** @@ -284,6 +354,11 @@ void _dom_node_ref(dom_node_internal *node) node->refcnt++; } + +/* ---------------------------------------------------------------------*/ + +/* The public virtual function of this interface Node */ + /** * Release a reference on a DOM node * @@ -291,15 +366,20 @@ void _dom_node_ref(dom_node_internal *node) * * If the reference count reaches zero and the node is not part of any * document, any memory claimed by the node will be released. + * + * If the parent of the node is NULL but the reference count does not reach + * zero, this means we should put this node to the document's deletion pending + * list. When the refcnt reach zero, we delete it. */ void _dom_node_unref(dom_node_internal *node) { + if (node == NULL) + return; + if (node->refcnt > 0) node->refcnt--; - if (node->refcnt == 0 && node->parent == NULL) { - dom_node_destroy(node); - } + dom_node_try_destroy(node); } /** @@ -316,50 +396,85 @@ void _dom_node_unref(dom_node_internal *node) dom_exception _dom_node_get_node_name(dom_node_internal *node, struct dom_string **result) { - struct dom_string *node_name; + struct dom_string *node_name, *temp; + dom_document *doc; + dom_exception err; + struct dom_resource_mgr rm; + + doc = node->owner; + /* Document Node and DocumentType Node can have no owner */ + assert(node->type == DOM_DOCUMENT_TYPE_NODE || + node->type == DOM_DOCUMENT_NODE || + doc != NULL); assert(node->name != NULL); + if (doc != NULL) { + _dom_document_get_resource_mgr(doc, &rm); + } else if (node->type == DOM_DOCUMENT_TYPE_NODE) { + _dom_document_type_get_resource_mgr( + (dom_document_type *) node, &rm); + } + /* If this node was created using a namespace-aware method and * has a defined prefix, then nodeName is a QName comprised * of prefix:name. */ - if ((node->type == DOM_ELEMENT_NODE || - node->type == DOM_ATTRIBUTE_NODE) && - node->prefix != NULL) { + if(node->prefix != NULL) { struct dom_string *colon; - dom_exception err; - err = dom_document_create_string(node->owner, + err = _dom_resource_mgr_create_string(&rm, (const uint8_t *) ":", SLEN(":"), &colon); if (err != DOM_NO_ERR) { return err; } + /* Make a temp prefix dom_string */ + err = _dom_resource_mgr_create_string_from_lwcstring(&rm, + node->prefix, &temp); + if (err != DOM_NO_ERR) { + dom_string_unref(colon); + return err; + } + /* Prefix + : */ - err = dom_string_concat(node->prefix, colon, &node_name); + err = dom_string_concat(temp, colon, &node_name); if (err != DOM_NO_ERR) { + dom_string_unref(temp); dom_string_unref(colon); return err; } + /*Finished with temp*/ + dom_string_unref(temp); /* Finished with colon */ dom_string_unref(colon); + /* Make a temp name dom_string */ + err = _dom_resource_mgr_create_string_from_lwcstring(&rm, + node->name, &temp); + if (err != DOM_NO_ERR) { + return err; + } /* Prefix + : + Localname */ - err = dom_string_concat(node_name, node->name, &colon); + err = dom_string_concat(node_name, temp, &colon); if (err != DOM_NO_ERR) { + dom_string_unref(temp); dom_string_unref(node_name); return err; } + /* Finished with temp */ + dom_string_unref(temp); /* Finished with intermediate node name */ dom_string_unref(node_name); node_name = colon; } else { - dom_string_ref(node->name); - - node_name = node->name; + err = _dom_resource_mgr_create_string_from_lwcstring(&rm, + node->name, &node_name); + if (err != DOM_NO_ERR) { + return err; + } } *result = node_name; @@ -384,10 +499,6 @@ dom_exception _dom_node_get_node_name(dom_node_internal *node, dom_exception _dom_node_get_node_value(dom_node_internal *node, struct dom_string **result) { - if (node->type == DOM_ATTRIBUTE_NODE) { - return dom_attr_get_value((struct dom_attr *) node, result); - } - if (node->value != NULL) dom_string_ref(node->value); @@ -412,6 +523,9 @@ dom_exception _dom_node_get_node_value(dom_node_internal *node, dom_exception _dom_node_set_node_value(dom_node_internal *node, struct dom_string *value) { + /* TODO + * Whether we should change this to a virtual function? + */ /* This is a NOP if the value is defined to be null. */ if (node->type == DOM_DOCUMENT_NODE || node->type == DOM_DOCUMENT_FRAGMENT_NODE || @@ -507,8 +621,8 @@ dom_exception _dom_node_get_child_nodes(dom_node_internal *node, if (node->owner == NULL) return DOM_NOT_SUPPORTED_ERR; - return dom_document_get_nodelist(node->owner, node, - NULL, NULL, NULL, result); + return _dom_document_get_nodelist(node->owner, DOM_NODELIST_CHILDREN, + node, NULL, NULL, NULL, result); } /** @@ -630,13 +744,10 @@ dom_exception _dom_node_get_next_sibling(dom_node_internal *node, dom_exception _dom_node_get_attributes(dom_node_internal *node, struct dom_namednodemap **result) { - if (node->type != DOM_ELEMENT_NODE) { - *result = NULL; - - return DOM_NO_ERR; - } + UNUSED(node); + *result = NULL; - return dom_element_get_attributes((struct dom_element *) node, result); + return DOM_NO_ERR; } /** @@ -654,8 +765,7 @@ dom_exception _dom_node_get_owner_document(dom_node_internal *node, struct dom_document **result) { /* Document nodes have no owner, as far as clients are concerned - * In reality, they own themselves as this simplifies code elsewhere - */ + * In reality, they own themselves as this simplifies code elsewhere */ if (node->type == DOM_DOCUMENT_NODE) { *result = NULL; @@ -664,7 +774,7 @@ dom_exception _dom_node_get_owner_document(dom_node_internal *node, /* If there is an owner, increase its reference count */ if (node->owner != NULL) - dom_node_ref((dom_node_internal *) node->owner); + dom_node_ref(node->owner); *result = node->owner; @@ -729,19 +839,10 @@ dom_exception _dom_node_insert_before(dom_node_internal *node, } /* Ensure that new_child is permitted as a child of node */ - if (!_dom_node_permitted_child(node, new_child)) + if (new_child->type != DOM_DOCUMENT_FRAGMENT_NODE && + !_dom_node_permitted_child(node, new_child)) return DOM_HIERARCHY_REQUEST_ERR; - /* DocumentType nodes are created outside the Document so, - * if we're trying to attach a DocumentType node, then we - * also need to set its owner. */ - if (node->type == DOM_DOCUMENT_NODE && - new_child->type == DOM_DOCUMENT_TYPE_NODE) { - /* See long comment in dom_node_initialise as to why - * we don't ref the document here */ - new_child->owner = (struct dom_document *) node; - } - /* Attempting to insert a node before itself is a NOP */ if (new_child == ref_child) { dom_node_ref(new_child); @@ -759,9 +860,19 @@ dom_exception _dom_node_insert_before(dom_node_internal *node, _dom_node_detach(new_child); } - /* If new_child is a DocumentFragment, insert its children + /* When a Node is attached, it should be removed from the pending + * list */ + dom_node_remove_pending(new_child); + + /* If new_child is a DocumentFragment, insert its children. * Otherwise, insert new_child */ if (new_child->type == DOM_DOCUMENT_FRAGMENT_NODE) { + /* Test the children of the docment fragment can be appended */ + dom_node_internal *c = new_child->first_child; + for (; c != NULL; c = c->next) + if (!_dom_node_permitted_child(node, c)) + return DOM_HIERARCHY_REQUEST_ERR; + if (new_child->first_child != NULL) { _dom_node_attach_range(new_child->first_child, new_child->last_child, @@ -783,6 +894,16 @@ dom_exception _dom_node_insert_before(dom_node_internal *node, : ref_child); } + /* DocumentType nodes are created outside the Document so, + * if we're trying to attach a DocumentType node, then we + * also need to set its owner. */ + if (node->type == DOM_DOCUMENT_NODE && + new_child->type == DOM_DOCUMENT_TYPE_NODE) { + /* See long comment in _dom_node_initialise as to why + * we don't ref the document here */ + new_child->owner = (struct dom_document *) node; + } + /** \todo Is it correct to return DocumentFragments? */ dom_node_ref(new_child); @@ -853,8 +974,21 @@ dom_exception _dom_node_replace_child(dom_node_internal *node, } /* Ensure that new_child is permitted as a child of node */ - if (!_dom_node_permitted_child(node, new_child)) - return DOM_HIERARCHY_REQUEST_ERR; + if (new_child->type == DOM_DOCUMENT_FRAGMENT_NODE) { + /* If this node is a doc fragment, we should test all its + * children nodes */ + dom_node_internal *c; + c = new_child->first_child; + while (c != NULL) { + if (!_dom_node_permitted_child(node, c)) + return DOM_HIERARCHY_REQUEST_ERR; + + c = c->next; + } + } else { + if (!_dom_node_permitted_child(node, new_child)) + return DOM_HIERARCHY_REQUEST_ERR; + } /* Attempting to replace a node with itself is a NOP */ if (new_child == old_child) { @@ -878,6 +1012,8 @@ dom_exception _dom_node_replace_child(dom_node_internal *node, /* Sort out the return value */ dom_node_ref(old_child); + /* The replaced node should be marded pending */ + dom_node_mark_pending(old_child); *result = old_child; return DOM_NO_ERR; @@ -922,8 +1058,12 @@ dom_exception _dom_node_remove_child(dom_node_internal *node, /* Detach the node */ _dom_node_detach(old_child); - /* Sort out the return value */ + /* 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(old_child); + dom_node_try_destroy(old_child); *result = old_child; return DOM_NO_ERR; @@ -1015,15 +1155,67 @@ dom_exception _dom_node_has_child_nodes(dom_node_internal *node, bool *result) * * \todo work out what happens when cloning Document, DocumentType, Entity * and Notation nodes. + * + * Note: we adopt a OO paradigm, this clone_node just provide a basic operation + * of clone. Special clones like Attr/EntitiReference stated above should + * provide their overload of this interface in their implementation file. */ dom_exception _dom_node_clone_node(dom_node_internal *node, bool deep, dom_node_internal **result) { - UNUSED(node); - UNUSED(deep); - UNUSED(result); + dom_node_internal *n, *child, *r; + dom_exception err; + dom_document *doc; + dom_user_data *ud; + + doc = node->owner; + assert(doc != NULL); + + err = dom_node_alloc(doc, node, &n); + if (err != DOM_NO_ERR) + return err; + + err = dom_node_copy(n, node); + if (err != DOM_NO_ERR) { + _dom_document_alloc(doc, n, 0); + return err; + } - return DOM_NOT_SUPPORTED_ERR; + if (deep) { + child = node->first_child; + while (child != NULL) { + err = dom_node_clone_node(child, deep, (void *) &r); + if (err != DOM_NO_ERR) { + dom_node_unref(n); + return err; + } + + err = dom_node_append_child(n, r, (void *) &r); + if (err != DOM_NO_ERR) { + dom_node_unref(n); + return err; + } + + /* Clean up the new node, we have reference it two + * times */ + dom_node_unref(r); + dom_node_unref(r); + child = child->next; + } + } + + *result = n; + + /* Call the dom_user_data_handlers */ + ud = node->user_data; + while (ud != NULL) { + if (ud->handler != NULL) + ud->handler(DOM_NODE_CLONED, ud->key, ud->data, + (dom_node *) node, (dom_node *) n); + ud = ud->next; + } + + return DOM_NO_ERR; } /** @@ -1038,9 +1230,36 @@ dom_exception _dom_node_clone_node(dom_node_internal *node, bool deep, */ dom_exception _dom_node_normalize(dom_node_internal *node) { - UNUSED(node); + dom_node_internal *n, *p; + dom_exception err; - return DOM_NOT_SUPPORTED_ERR; + p = node->first_child; + if (p == NULL) + return DOM_NO_ERR; + + n = p->next; + + while (n != NULL) { + if (n->type == DOM_TEXT_NODE && p->type == DOM_TEXT_NODE) { + err = _dom_merge_adjacent_text(p, n); + if (err != DOM_NO_ERR) + return err; + + _dom_node_detach(n); + dom_node_unref(n); + n = p->next; + continue; + } + if (n->type != DOM_TEXT_NODE) { + err = dom_node_normalize(n); + if (err != DOM_NO_ERR) + return err; + } + p = n; + n = n->next; + } + + return DOM_NO_ERR; } /** @@ -1054,15 +1273,22 @@ dom_exception _dom_node_normalize(dom_node_internal *node) * \return DOM_NO_ERR. */ dom_exception _dom_node_is_supported(dom_node_internal *node, - struct dom_string *feature, dom_node_internal *version, + struct dom_string *feature, struct dom_string *version, bool *result) { - UNUSED(node); - UNUSED(feature); - UNUSED(version); - UNUSED(result); + dom_document *doc; + dom_implementation *impl; + bool has; - return DOM_NOT_SUPPORTED_ERR; + doc = node->owner; + assert(doc != NULL); + dom_document_get_implementation(doc, &impl); + assert(impl != NULL); + dom_implementation_has_feature(impl, feature, version, &has); + + *result = has; + + return DOM_NO_ERR; } /** @@ -1079,13 +1305,18 @@ dom_exception _dom_node_is_supported(dom_node_internal *node, dom_exception _dom_node_get_namespace(dom_node_internal *node, struct dom_string **result) { + lwc_context *ctx; + + assert(node->owner != NULL); + ctx = _dom_document_get_intern_context(node->owner); + assert(ctx != NULL); + /* If there is a namespace, increase its reference count */ if (node->namespace != NULL) - dom_string_ref(node->namespace); + lwc_context_string_ref(ctx, node->namespace); - *result = node->namespace; - - return DOM_NO_ERR; + return _dom_document_create_string_from_lwcstring(node->owner, + node->namespace, result); } /** @@ -1102,13 +1333,19 @@ dom_exception _dom_node_get_namespace(dom_node_internal *node, dom_exception _dom_node_get_prefix(dom_node_internal *node, struct dom_string **result) { + lwc_context *ctx; + + assert(node->owner != NULL); + ctx = _dom_document_get_intern_context(node->owner); + assert(ctx != NULL); + /* If there is a prefix, increase its reference count */ if (node->prefix != NULL) - dom_string_ref(node->prefix); + lwc_context_string_ref(ctx, node->prefix); - *result = node->prefix; - - return DOM_NO_ERR; + return _dom_document_create_string_from_lwcstring(node->owner, + node->prefix, + result); } /** @@ -1137,6 +1374,13 @@ dom_exception _dom_node_get_prefix(dom_node_internal *node, dom_exception _dom_node_set_prefix(dom_node_internal *node, struct dom_string *prefix) { + dom_exception err; + lwc_string *str; + lwc_context *docctx; + + docctx = _dom_document_get_intern_context(node->owner); + assert(docctx != NULL); + /* Only Element and Attribute nodes created using * namespace-aware methods may have a prefix */ if ((node->type != DOM_ELEMENT_NODE && @@ -1154,7 +1398,7 @@ dom_exception _dom_node_set_prefix(dom_node_internal *node, /* No longer want existing prefix */ if (node->prefix != NULL) { - dom_string_unref(node->prefix); + lwc_context_string_unref(docctx, node->prefix); } /* Set the prefix */ @@ -1163,8 +1407,11 @@ dom_exception _dom_node_set_prefix(dom_node_internal *node, if (dom_string_length(prefix) == 0) { node->prefix = NULL; } else { - dom_string_ref(prefix); - node->prefix = prefix; + err = _dom_node_get_intern_string(node, prefix, &str); + if (err != DOM_NO_ERR) + return err; + + node->prefix = str; } } else { node->prefix = NULL; @@ -1186,7 +1433,13 @@ dom_exception _dom_node_set_prefix(dom_node_internal *node, */ dom_exception _dom_node_get_local_name(dom_node_internal *node, struct dom_string **result) -{ +{ + lwc_context *ctx; + + assert(node->owner != NULL); + ctx = _dom_document_get_intern_context(node->owner); + assert(ctx != NULL); + /* Only Element and Attribute nodes may have a local name */ if (node->type != DOM_ELEMENT_NODE && node->type != DOM_ATTRIBUTE_NODE) { @@ -1194,19 +1447,13 @@ dom_exception _dom_node_get_local_name(dom_node_internal *node, return DOM_NO_ERR; } - /* Node must have been created using a namespace-aware method */ - if (node->namespace == NULL) { - *result = NULL; - return DOM_NO_ERR; - } - /* The node may have a local name, reference it if so */ if (node->name != NULL) { - dom_string_ref(node->name); + lwc_context_string_ref(ctx, node->name); } - *result = node->name; - return DOM_NO_ERR; + return _dom_document_create_string_from_lwcstring(node->owner, + node->name, result); } /** @@ -1218,13 +1465,10 @@ dom_exception _dom_node_get_local_name(dom_node_internal *node, */ dom_exception _dom_node_has_attributes(dom_node_internal *node, bool *result) { - if (node->type != DOM_ELEMENT_NODE) { - *result = false; - - return DOM_NO_ERR; - } + UNUSED(node); + *result = false; - return dom_element_has_attributes((struct dom_element *) node, result); + return DOM_NO_ERR; } /** @@ -1237,6 +1481,9 @@ dom_exception _dom_node_has_attributes(dom_node_internal *node, bool *result) * The returned string will have its reference count increased. It is * the responsibility of the caller to unref the string once it has * finished with it. + * + * We don't support this API now, so this function call should always + * return DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_node_get_base(dom_node_internal *node, struct dom_string **result) @@ -1258,6 +1505,9 @@ dom_exception _dom_node_get_base(dom_node_internal *node, * implementations. * * The result is a bitfield of dom_document_position values. + * + * We don't support this API now, so this function call should always + * return DOM_NOT_SUPPORTED_ERR. */ dom_exception _dom_node_compare_document_position(dom_node_internal *node, dom_node_internal *other, uint16_t *result) @@ -1286,10 +1536,21 @@ dom_exception _dom_node_compare_document_position(dom_node_internal *node, dom_exception _dom_node_get_text_content(dom_node_internal *node, struct dom_string **result) { - UNUSED(node); - UNUSED(result); + dom_node_internal *n; + dom_string *str; + dom_string *ret; - return DOM_NOT_SUPPORTED_ERR; + assert(node->owner != NULL); + + for (n = node->first_child; n != NULL; n = n->next) { + dom_node_get_text_content(n, &ret); + dom_string_concat(str, ret, &str); + } + + dom_string_ref(str); + *result = str; + + return DOM_NO_ERR; } /** @@ -1306,10 +1567,36 @@ dom_exception _dom_node_get_text_content(dom_node_internal *node, dom_exception _dom_node_set_text_content(dom_node_internal *node, struct dom_string *content) { - UNUSED(node); - UNUSED(content); + dom_node_internal *n, *p, *r; + dom_document *doc; + dom_text *text; + dom_exception err; + + n = node->first_child; + + while (n != NULL) { + p = n; + n = n->next; + /* Add the (void *) casting to avoid gcc warning: + * dereferencing type-punned pointer will break + * strict-aliasing rules */ + err = dom_node_remove_child(node, n, (void *) &r); + if (err != DOM_NO_ERR) + return err; + } - return DOM_NOT_SUPPORTED_ERR; + doc = node->owner; + assert(doc != NULL); + + err = dom_document_create_text_node(doc, content, &text); + if (err != DOM_NO_ERR) + return err; + + err = dom_node_append_child(node, text, (void *) &r); + if (err != DOM_NO_ERR) + return err; + + return DOM_NO_ERR; } /** @@ -1322,8 +1609,8 @@ dom_exception _dom_node_set_text_content(dom_node_internal *node, * * This tests if the two nodes reference the same object. */ -dom_exception _dom_node_is_same(dom_node_internal *node, dom_node_internal *other, - bool *result) +dom_exception _dom_node_is_same(dom_node_internal *node, + dom_node_internal *other, bool *result) { *result = (node == other); @@ -1345,11 +1632,12 @@ dom_exception _dom_node_is_same(dom_node_internal *node, dom_node_internal *othe dom_exception _dom_node_lookup_prefix(dom_node_internal *node, struct dom_string *namespace, struct dom_string **result) { - UNUSED(node); - UNUSED(namespace); - UNUSED(result); + if (node->parent != NULL) + return dom_node_lookup_prefix(node, namespace, result); + else + *result = NULL; - return DOM_NOT_SUPPORTED_ERR; + return DOM_NO_ERR; } /** @@ -1363,11 +1651,11 @@ dom_exception _dom_node_lookup_prefix(dom_node_internal *node, dom_exception _dom_node_is_default_namespace(dom_node_internal *node, struct dom_string *namespace, bool *result) { - UNUSED(node); - UNUSED(namespace); - UNUSED(result); - - return DOM_NOT_SUPPORTED_ERR; + if (node->parent != NULL) + return dom_node_is_default_namespace(node, namespace, result); + else + *result = false; + return DOM_NO_ERR; } /** @@ -1385,11 +1673,12 @@ dom_exception _dom_node_is_default_namespace(dom_node_internal *node, dom_exception _dom_node_lookup_namespace(dom_node_internal *node, struct dom_string *prefix, struct dom_string **result) { - UNUSED(node); - UNUSED(prefix); - UNUSED(result); + if (node->parent != NULL) + return dom_node_lookup_namespace(node->parent, prefix, result); + else + *result = NULL; - return DOM_NOT_SUPPORTED_ERR; + return DOM_NO_ERR; } /** @@ -1410,15 +1699,106 @@ dom_exception _dom_node_lookup_namespace(dom_node_internal *node, * + publicId, systemId, internalSubset are equal * + The node entities are equal * + The node notations are equal + * TODO: in document_type, we should override this virtual function */ dom_exception _dom_node_is_equal(dom_node_internal *node, dom_node_internal *other, bool *result) { - UNUSED(node); - UNUSED(other); - UNUSED(result); + dom_exception err; + dom_string *s1, *s2; + lwc_context *c1, *c2; + dom_namednodemap *m1, *m2; + dom_nodelist *l1, *l2; - return DOM_NOT_SUPPORTED_ERR; + if (node->type != other->type){ + *result = false; + return DOM_NO_ERR; + } + + assert(node->owner != NULL); + assert(other->owner != NULL); + + err = dom_node_get_node_name(node, &s1); + if (err != DOM_NO_ERR) + return err; + + err = dom_node_get_node_name(other, &s2); + if (err != DOM_NO_ERR) + return err; + + if (dom_string_cmp(s1, s2) != 0) { + *result = false; + return DOM_NO_ERR; + } + + c1 = _dom_document_get_intern_context(node->owner); + assert(c1 != NULL); + + c2 = _dom_document_get_intern_context(other->owner); + assert(c2 != NULL); + + if (c1 == c2) { + if (node->name != other->name || + node->namespace != other->namespace || + node->prefix != other->prefix) { + *result = false; + return DOM_NO_ERR; + } + } else { + if (_dom_lwc_string_compare_raw(node->name, other->name) != 0){ + *result = false; + return DOM_NO_ERR; + } + + if (_dom_lwc_string_compare_raw(node->namespace, + other->namespace) != 0){ + *result = false; + return DOM_NO_ERR; + } + + if (_dom_lwc_string_compare_raw(node->prefix, + other->prefix) != 0){ + *result = false; + return DOM_NO_ERR; + } + } + + if (dom_string_cmp(node->value, other->value) != 0) { + *result = false; + return DOM_NO_ERR; + } + + // Following comes the attributes + err = dom_node_get_attributes(node, &m1); + if (err != DOM_NO_ERR) + return err; + + err = dom_node_get_attributes(other, &m2); + if (err != DOM_NO_ERR) + return err; + + if (dom_namednodemap_equal(m1, m2) != true) { + *result = false; + return DOM_NO_ERR; + } + + // Finally the childNodes + err = dom_node_get_child_nodes(node, &l1); + if (err != DOM_NO_ERR) + return err; + + err = dom_node_get_child_nodes(other, &l2); + if (err != DOM_NO_ERR) + return err; + + if (dom_nodelist_equal(l1, l2) != true) { + *result = false; + return DOM_NO_ERR; + } + + *result = true; + + return DOM_NO_ERR; } /** @@ -1435,12 +1815,23 @@ dom_exception _dom_node_get_feature(dom_node_internal *node, struct dom_string *feature, struct dom_string *version, void **result) { - UNUSED(node); - UNUSED(feature); - UNUSED(version); - UNUSED(result); + dom_document *doc; + dom_implementation *impl; + bool has; + + doc = node->owner; + assert(doc != NULL); + dom_document_get_implementation(doc, &impl); + assert(impl != NULL); + dom_implementation_has_feature(impl, feature, version, &has); + + if (has) { + *result = node; + } else { + *result = NULL; + } - return DOM_NOT_SUPPORTED_ERR; + return DOM_NO_ERR; } /** @@ -1479,14 +1870,14 @@ dom_exception _dom_node_set_user_data(dom_node_internal *node, *result = ud->data; - dom_document_alloc(node->owner, ud, 0); + _dom_document_alloc(node->owner, ud, 0); return DOM_NO_ERR; } /* Otherwise, create a new user data object if one wasn't found */ if (ud == NULL) { - ud = dom_document_alloc(node->owner, NULL, + ud = _dom_document_alloc(node->owner, NULL, sizeof(struct dom_user_data)); if (ud == NULL) return DOM_NO_MEM_ERR; @@ -1542,9 +1933,124 @@ dom_exception _dom_node_get_user_data(dom_node_internal *node, return DOM_NO_ERR; } -/* */ -/*----------------------------------------------------------------------------*/ -/* */ + +/*--------------------------------------------------------------------------*/ + +/* The protected virtual functions */ + +/* We should never call this pure-virtual function directly */ +dom_exception _dom_node_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + UNUSED(doc); + UNUSED(n); + UNUSED(ret); + + return DOM_NOT_SUPPORTED_ERR; +} + + +/* Copy the internal attributes of a Node from old to new */ +dom_exception _dom_node_copy(dom_node_internal *new, dom_node_internal *old) +{ + lwc_context *nctx, *octx; + dom_exception err; + + new->vtable = old->vtable; + new->base.vtable = old->base.vtable; + + assert(old->owner != NULL); + octx = _dom_document_get_intern_context(old->owner); + assert(octx != NULL); + + assert(new->owner != NULL); + nctx = _dom_document_get_intern_context(old->owner); + assert(nctx != NULL); + + new->type = old->type; + new->parent = NULL; + new->first_child = NULL; + new->last_child = NULL; + new->previous = NULL; + new->next = NULL; + new->owner = old->owner; + + if (octx == nctx) { + lwc_context_string_ref(octx, old->name); + new->name = old->name; + + if (old->namespace != NULL) + lwc_context_string_ref(octx, old->namespace); + new->namespace = old->namespace; + + if (old->prefix != NULL) + lwc_context_string_ref(octx, old->prefix); + new->prefix = old->prefix; + } else { + lwc_string *str; + lwc_error lerr; + + lerr = lwc_context_intern(nctx, lwc_string_data(old->name), + lwc_string_length(old->name), &str); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); + + new->name = str; + + if (old->namespace != NULL) { + lerr = lwc_context_intern(nctx, + lwc_string_data(old->namespace), + lwc_string_length(old->namespace), + &str); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); + + new->namespace = str; + } else + new->namespace = NULL; + + if (old->prefix != NULL) { + lerr = lwc_context_intern(nctx, + lwc_string_data(old->prefix), + lwc_string_length(old->prefix), &str); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); + + new->prefix = str; + } else + new->prefix = NULL; + } + + dom_alloc al; + void *pw; + + if (old->value != NULL) { + _dom_document_get_allocator(new->owner, &al, &pw); + dom_string *value; + err = dom_string_clone(al, pw, old->value, &value); + if (err != DOM_NO_ERR) + return err; + + new->value = value; + } else { + new->value = NULL; + } + + new->user_data = NULL; + new->refcnt = 1; + + list_init(&new->pending_list); + /* The new copyed node has no parent, + * so it should be put in the pending list. */ + dom_node_mark_pending(new); + + return DOM_NO_ERR; +} + + +/*--------------------------------------------------------------------------*/ + +/* The helper functions */ /** * Determine if a node is permitted as a child of another node @@ -1626,20 +2132,18 @@ bool _dom_node_permitted_child(const dom_node_internal *parent, */ bool _dom_node_readonly(const dom_node_internal *node) { - /* DocumentType and Notation nodes are read only */ - if (node->type == DOM_DOCUMENT_TYPE_NODE || - node->type == DOM_NOTATION_NODE) - return true; + const dom_node_internal *n = node; - /* Entity nodes and their descendants are read only */ - for (; node != NULL; node = node->parent) { - if (node->type == DOM_ENTITY_NODE) - return true; - } + /* DocumentType and Notation ns are read only */ + if (n->type == DOM_DOCUMENT_TYPE_NODE || + n->type == DOM_NOTATION_NODE) + return true; - /* EntityReference nodes and their descendants are read only */ - for (; node != NULL; node = node->parent) { - if (node->type == DOM_ENTITY_REFERENCE_NODE) + /* Entity ns and their descendants are read only + * EntityReference ns and their descendants are read only */ + for (n = node; n != NULL; n = n->parent) { + if (n->type == DOM_ENTITY_NODE + || n->type == DOM_ENTITY_REFERENCE_NODE) return true; } @@ -1668,6 +2172,10 @@ void _dom_node_attach(dom_node_internal *node, dom_node_internal *parent, */ void _dom_node_detach(dom_node_internal *node) { + /* When a Node is not in the document tree, it must be in the + pending list */ + dom_node_mark_pending(node); + _dom_node_detach_range(node, node); } @@ -1701,8 +2209,9 @@ void _dom_node_attach_range(dom_node_internal *first, else parent->last_child = last; - for (dom_node_internal *n = first; n != last->next; n = n->next) + for (dom_node_internal *n = first; n != last->next; n = n->next) { n->parent = parent; + } } /** @@ -1726,8 +2235,9 @@ void _dom_node_detach_range(dom_node_internal *first, else last->parent->last_child = first->previous; - for (dom_node_internal *n = first; n != last->next; n = n->next) + for (dom_node_internal *n = first; n != last->next; n = n->next) { n->parent = NULL; + } first->previous = NULL; last->next = NULL; @@ -1771,9 +2281,220 @@ void _dom_node_replace(dom_node_internal *old, else old->parent->last_child = last; - for (dom_node_internal *n = first; n != last->next; n = n->next) + for (dom_node_internal *n = first; n != last->next; n = n->next) { n->parent = old->parent; + } old->previous = old->next = old->parent = NULL; } +/** + * Migrate one lwc_string from one context to another, this function + * may be used when we import/adopt a Node between documents. + * + * \param old The source context + * \param new The new context + * \param string The lwc_string to migrate + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _redocument_lwcstring(lwc_context *old, lwc_context *new, + lwc_string **string) +{ + lwc_string *str; + lwc_error lerr; + + lerr = lwc_context_intern(new, lwc_string_data(*string), + lwc_string_length(*string), &str); + if (lerr != lwc_error_ok) + return _dom_exception_from_lwc_error(lerr); + + lwc_context_string_unref(old, *string); + *string = str; + + return DOM_NO_ERR; +} + +/** + * Migrate one dom_string from one document to another, this function + * may be used when we import/adopt a Node between documents. + * + * \param old The source document + * \param new The new document + * \param string The dom_string to migrate + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _redocument_domstring(dom_document *old, dom_document* new, + dom_string **string) +{ + dom_exception err; + dom_string *str; + + UNUSED(old); + err = _dom_document_create_string(new, NULL, 0, &str); + if (err != DOM_NO_ERR) + return err; + + err = dom_string_dup(*string, &str); + if (err != DOM_NO_ERR) + return err; + + dom_string_unref(*string); + *string = str; + + return DOM_NO_ERR; +} + +/** + * Merge two adjacent text nodes into one text node. + * + * \param p The first text node + * \param n The second text node + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_merge_adjacent_text(dom_node_internal *p, + dom_node_internal *n) +{ + assert(p->type = DOM_TEXT_NODE); + assert(n->type = DOM_TEXT_NODE); + + dom_string *str; + dom_exception err; + + err = dom_text_get_whole_text(n, &str); + if (err != DOM_NO_ERR) + return err; + + err = dom_characterdata_append_data(p, str); + if (err != DOM_NO_ERR) + return err; + + dom_string_unref(str); + + return DOM_NO_ERR; +} + +/** + * Intern a dom_string using the node's owner document's lwc_context + * + * \param node The node + * \param str The dom_string to be interned + * \param intern The returned interned string + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_node_get_intern_string(dom_node_internal *node, + dom_string *str, lwc_string **intern) +{ + dom_exception err; + lwc_context *ctx, *docctx; + lwc_string *ret; + + assert(str != NULL); + assert(node->owner != NULL); + + docctx = _dom_document_get_intern_context(node->owner); + assert(docctx != NULL); + + err = dom_string_get_intern(str, &ctx, &ret); + if (err != DOM_NO_ERR) + return err; + + if (ctx != docctx) { + err = _dom_string_intern(str, docctx, &ret); + if (err != DOM_NO_ERR) + return err; + } + + *intern = ret; + + return DOM_NO_ERR; +} + +/** + * Unref a lwc_string used in this node + * + * \param node The node + * \param intern The lwc_string to unref + */ +void _dom_node_unref_intern_string(dom_node_internal *node, + struct lwc_string_s *intern) +{ + struct dom_resource_mgr rm; + struct dom_document *doc = node->owner; + + if (doc != NULL) { + _dom_document_get_resource_mgr(doc, &rm); + } else if (node->type == DOM_DOCUMENT_TYPE_NODE) { + _dom_document_type_get_resource_mgr( + (dom_document_type *) node, &rm); + } + + lwc_context_string_unref(rm.ctx, intern); +} + +/** + * Try to destroy this node. + * + * \param node The node to destroy + * + * When some node owns this node, (such as an elment owns its attribute nodes) + * when this node being not owned, the owner should call this function to try + * to destroy this node. + * + * @note: Owning a node does not means this node's refcnt is above zero. + */ +void _dom_node_try_destroy(dom_node_internal *node) +{ + if (node == NULL) + return; + + if (node->parent == NULL) { + if (node->refcnt == 0) { + dom_node_destroy(node); + } else if (node->pending_list.prev == &node->pending_list){ + assert (node->pending_list.next == &node->pending_list); + list_append(&node->owner->pending_nodes, + &node->pending_list); + } + } +} + +/** + * To add some node to the pending list, when a node is removed from its parent + * or an attribute is removed from its element + * + * \param node The Node instance + */ +void _dom_node_mark_pending(dom_node_internal *node) +{ + struct dom_document *doc = node->owner; + + /* TODO: the pending_list is located at in dom_document, but some + * nodes can be created without a document created, such as a + * dom_document_type node. For this reason, we should test whether + * the doc is NULL. */ + if (doc != NULL) { + /* The node must not be in the pending list */ + assert(node->pending_list.prev == &node->pending_list); + + list_append(&doc->pending_nodes, &node->pending_list); + } +} + +/** + * To remove the node from the pending list, this may happen when + * a node is removed and then appended to another parent + * + * \param node The Node instance + */ +void _dom_node_remove_pending(dom_node_internal *node) +{ + struct dom_document *doc = node->owner; + + if (doc != NULL) { + /* The node must be in the pending list */ + assert(node->pending_list.prev != &node->pending_list); + + list_del(&node->pending_list); + } +} + diff --git a/src/core/node.h b/src/core/node.h index 27c9f35..68776da 100644 --- a/src/core/node.h +++ b/src/core/node.h @@ -10,7 +10,12 @@ #include +#include + #include +#include + +#include "utils/list.h" /** * User data context attached to a DOM node @@ -23,6 +28,23 @@ struct dom_user_data { struct dom_user_data *next; /**< Next in list */ struct dom_user_data *prev; /**< Previous in list */ }; +typedef struct dom_user_data dom_user_data; + +/** + * The internally used virtual function table. + */ +typedef struct dom_node_protect_vtable { + + void (*destroy)(dom_node_internal *n); + /**< The destroy virtual function, it + * should be private to client */ + dom_exception (*alloc)(struct dom_document *doc, + dom_node_internal *n, dom_node_internal **ret); + /**< Allocate the memory of the new Node */ + dom_exception (*copy)(dom_node_internal *new, dom_node_internal *old); + /**< Copy the old to new as well as + * all its attributes, but not its children */ +} dom_node_protect_vtable; /** * The real DOM node object @@ -31,13 +53,11 @@ struct dom_user_data { */ struct dom_node_internal { struct dom_node base; /**< The vtable base */ - void (*destroy)(dom_node_internal *n); - /**< The destroy vitual function, it - * should be privated to client */ + void *vtable; /**< The protected vtable */ - struct dom_string *name; /**< Node name (this is the local part - * of a QName in the cases where a - * namespace exists) */ + struct lwc_string_s *name; /**< Node name (this is the local part + * of a QName in the cases where a + * namespace exists) */ struct dom_string *value; /**< Node value */ dom_node_type type; /**< Node type */ dom_node_internal *parent; /**< Parent node */ @@ -48,27 +68,37 @@ struct dom_node_internal { struct dom_document *owner; /**< Owning document */ - struct dom_string *namespace; /**< Namespace URI */ - struct dom_string *prefix; /**< Namespace prefix */ + struct lwc_string_s *namespace; /**< Namespace URI */ + struct lwc_string_s *prefix; /**< Namespace prefix */ struct dom_user_data *user_data; /**< User data list */ uint32_t refcnt; /**< Reference count */ + + struct list_entry pending_list; /**< The document delete pending list */ }; -dom_node_internal * dom_node_create(struct dom_document *doc); +dom_node_internal * _dom_node_create(struct dom_document *doc); -dom_exception dom_node_initialise(struct dom_node_internal *node, +dom_exception _dom_node_initialise(struct dom_node_internal *node, struct dom_document *doc, dom_node_type type, - struct dom_string *name, struct dom_string *value, - struct dom_string *namespace, struct dom_string *prefix); + struct lwc_string_s *name, struct dom_string *value, + struct lwc_string_s *namespace, struct lwc_string_s *prefix); -void dom_node_finalise(struct dom_document *doc, dom_node_internal *node); +dom_exception _dom_node_initialise_generic( + struct dom_node_internal *node, struct dom_document *doc, + dom_alloc alloc, void *pw, struct lwc_context_s *ctx, + dom_node_type type, struct lwc_string_s *name, + struct dom_string *value, struct lwc_string_s *namespace, + struct lwc_string_s *prefix); + +void _dom_node_finalise(struct dom_document *doc, dom_node_internal *node); +void _dom_node_finalise_generic(dom_node_internal *node, dom_alloc alloc, + void *pw, struct lwc_context_s *ctx); bool _dom_node_readonly(const dom_node_internal *node); /* The DOM Node's vtable methods */ -void _dom_node_destroy(struct dom_node_internal *node); dom_exception _dom_node_get_node_name(dom_node_internal *node, struct dom_string **result); dom_exception _dom_node_get_node_value(dom_node_internal *node, @@ -110,7 +140,7 @@ dom_exception _dom_node_clone_node(dom_node_internal *node, bool deep, dom_node_internal **result); dom_exception _dom_node_normalize(dom_node_internal *node); dom_exception _dom_node_is_supported(dom_node_internal *node, - struct dom_string *feature, dom_node_internal *version, + struct dom_string *feature, struct dom_string *version, bool *result); dom_exception _dom_node_get_namespace(dom_node_internal *node, struct dom_string **result); @@ -129,8 +159,8 @@ dom_exception _dom_node_get_text_content(dom_node_internal *node, struct dom_string **result); dom_exception _dom_node_set_text_content(dom_node_internal *node, struct dom_string *content); -dom_exception _dom_node_is_same(dom_node_internal *node, dom_node_internal *other, - bool *result); +dom_exception _dom_node_is_same(dom_node_internal *node, + dom_node_internal *other, bool *result); dom_exception _dom_node_lookup_prefix(dom_node_internal *node, struct dom_string *namespace, struct dom_string **result); dom_exception _dom_node_is_default_namespace(dom_node_internal *node, @@ -188,11 +218,85 @@ dom_exception _dom_node_get_user_data(dom_node_internal *node, _dom_node_get_user_data +/* Following comes the protected vtable */ +void _dom_node_destroy(struct dom_node_internal *node); +dom_exception _dom_node_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret); +dom_exception _dom_node_copy(struct dom_node_internal *new, + struct dom_node_internal *old); + +#define DOM_NODE_PROTECT_VTABLE \ + _dom_node_destroy, \ + _dom_node_alloc, \ + _dom_node_copy + + /* The destroy API should be used inside DOM module */ -static inline void dom_node_destroy(struct dom_node *node) +static inline void dom_node_destroy(struct dom_node_internal *node) +{ + ((dom_node_protect_vtable *) node->vtable)->destroy(node); +} +#define dom_node_destroy(n) dom_node_destroy((dom_node_internal *) (n)) + +/* Allocate the Node */ +static inline dom_exception dom_node_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + return ((dom_node_protect_vtable *) n->vtable)->alloc(doc, n, ret); +} +#define dom_node_alloc(d,n,r) dom_node_alloc((struct dom_document *) (d), \ + (dom_node_internal *) (n), (dom_node_internal **) (r)) + + +/* Copy the Node old to new */ +static inline dom_exception dom_node_copy(struct dom_node_internal *new, + struct dom_node_internal *old) { - ((dom_node_internal *) node)->destroy((dom_node_internal *) node); + return ((dom_node_protect_vtable *) old->vtable)->copy(new, old); } -#define dom_node_destroy(n) dom_node_destroy((dom_node *) (n)) +#define dom_node_copy(n,o) dom_node_copy((dom_node_internal *) (n), \ + (dom_node_internal *) (o)) + +/* Following are some helper functions */ +#define dom_node_get_owner(n) ((dom_node_internal *) (n))->owner + +#define dom_node_set_owner(n, d) ((dom_node_internal *) (n))->owner = \ + (struct dom_document *) (d) + +#define dom_node_get_parent(n) ((dom_node_internal *) (n))->parent + +#define dom_node_set_parent(n, p) ((dom_node_internal *) (n))->parent = \ + (dom_node_internal *) (p) + +#define dom_node_get_refcount(n) ((dom_node_internal *) (n))->refcnt + +dom_exception _redocument_lwcstring(lwc_context *old, lwc_context *new, + lwc_string **string); +dom_exception _redocument_domstring(struct dom_document *old, + struct dom_document* new, struct dom_string **string); +dom_exception _dom_merge_adjacent_text(dom_node_internal *p, + dom_node_internal *n); +/* Used to extract the lwc_string from dom_string. + * If there is no lwc_string inside the param, create one use the node->owner + * as document */ +dom_exception _dom_node_get_intern_string(dom_node_internal *node, + struct dom_string *str, struct lwc_string_s **intern); +void _dom_node_unref_intern_string(dom_node_internal *node, + struct lwc_string_s *inter); + +/* Try to destroy the node, if its refcnt is not zero, then append it to the + * owner document's pending list */ +void _dom_node_try_destroy(dom_node_internal *node); +#define dom_node_try_destroy(n) _dom_node_try_destroy((dom_node_internal *) (n)) + +/* To add some node to the pending list */ +void _dom_node_mark_pending(dom_node_internal *node); +#define dom_node_mark_pending(n) _dom_node_mark_pending(\ + (dom_node_internal *) (n)) +/* To remove the node from the pending list, this may happen when + * a node is removed and then appended to another parent */ +void _dom_node_remove_pending(dom_node_internal *node); +#define dom_node_remove_pending(n) _dom_node_remove_pending(\ + (dom_node_internal *) (n)) #endif diff --git a/src/core/nodelist.c b/src/core/nodelist.c index 16534b6..2497619 100644 --- a/src/core/nodelist.c +++ b/src/core/nodelist.c @@ -3,12 +3,17 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ +#include #include +#include #include #include +#include + #include "core/document.h" #include "core/node.h" #include "core/nodelist.h" @@ -21,18 +26,22 @@ struct dom_nodelist { struct dom_document *owner; /**< Owning document */ - struct dom_node_internal *root; /**< Root of applicable subtree */ + struct dom_node_internal *root; + /**< Root of applicable subtree */ - enum { DOM_NODELIST_CHILDREN, - DOM_NODELIST_BY_NAME, - DOM_NODELIST_BY_NAMESPACE - } type; /**< List type */ + nodelist_type type; /**< Type of this list */ union { - struct dom_string *name; /**< Tag name to match */ struct { - struct dom_string *namespace; /**< Namespace */ - struct dom_string *localname; /**< Localname */ + struct lwc_string_s *name; + /**< Tag name to match */ + bool any_name; /**< The name is '*' */ + } n; + struct { + bool any_namespace; /**< The namespace is '*' */ + bool any_localname; /**< The localname is '*' */ + struct lwc_string_s *namespace; /**< Namespace */ + struct lwc_string_s *localname; /**< Localname */ } ns; /**< Data for namespace matching */ } data; @@ -43,6 +52,7 @@ struct dom_nodelist { * Create a nodelist * * \param doc Owning document + * \param type The type of the NodeList * \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) @@ -52,27 +62,21 @@ struct dom_nodelist { * * ::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 must unref the list once finished with it. */ -dom_exception dom_nodelist_create(struct dom_document *doc, - struct dom_node_internal *root, struct dom_string *tagname, - struct dom_string *namespace, struct dom_string *localname, +dom_exception _dom_nodelist_create(struct dom_document *doc, nodelist_type type, + struct dom_node_internal *root, struct lwc_string_s *tagname, + struct lwc_string_s *namespace, struct lwc_string_s *localname, struct dom_nodelist **list) { struct dom_nodelist *l; + lwc_context *ctx; + + ctx = _dom_document_get_intern_context(doc); + assert(ctx != NULL); - l = dom_document_alloc(doc, NULL, sizeof(struct dom_nodelist)); + l = _dom_document_alloc(doc, NULL, sizeof(struct dom_nodelist)); if (l == NULL) return DOM_NO_MEM_ERR; @@ -82,20 +86,45 @@ dom_exception dom_nodelist_create(struct dom_document *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->type = type; + + if (type == DOM_NODELIST_BY_NAME) { + assert(tagname != NULL); + l->data.n.any_name = false; + if (lwc_string_length(tagname) == 1) { + const char *ch = lwc_string_data(tagname); + if (*ch == '*') { + l->data.n.any_name = true; + } + } + + lwc_context_string_ref(ctx, tagname); + l->data.n.name = tagname; + } else if (type == DOM_NODELIST_BY_NAMESPACE) { + l->data.ns.any_localname = false; + l->data.ns.any_namespace = false; + if (localname != NULL) { + if (lwc_string_length(localname) == 1) { + const char *ch = lwc_string_data(localname); + if (*ch == '*') { + l->data.ns.any_localname = true; + } + } + lwc_context_string_ref(ctx, localname); + } + if (namespace != NULL) { + if (lwc_string_length(namespace) == 1) { + const char *ch = lwc_string_data(namespace); + if (*ch == '*') { + l->data.ns.any_namespace = true; + } + } + lwc_context_string_ref(ctx, namespace); + } + l->data.ns.namespace = namespace; l->data.ns.localname = localname; - } else { - l->type = DOM_NODELIST_CHILDREN; - } + } l->refcnt = 1; @@ -111,6 +140,7 @@ dom_exception dom_nodelist_create(struct dom_document *doc, */ void dom_nodelist_ref(struct dom_nodelist *list) { + assert(list != NULL); list->refcnt++; } @@ -124,30 +154,41 @@ void dom_nodelist_ref(struct dom_nodelist *list) */ void dom_nodelist_unref(struct dom_nodelist *list) { + if (list == NULL) + return; + if (--list->refcnt == 0) { struct dom_node_internal *owner = (struct dom_node_internal *) list->owner; + lwc_context *ctx; + ctx = _dom_document_get_intern_context((dom_document *) owner); + assert(ctx != NULL); 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); + if (list->data.ns.namespace != NULL) + lwc_context_string_unref(ctx, + list->data.ns.namespace); + if (list->data.ns.localname != NULL) + lwc_context_string_unref(ctx, + list->data.ns.localname); break; case DOM_NODELIST_BY_NAME: - dom_string_unref(list->data.name); + assert(list->data.n.name != NULL); + lwc_context_string_unref(ctx, list->data.n.name); break; } dom_node_unref(list->root); /* Remove list from document */ - dom_document_remove_nodelist(list->owner, list); + _dom_document_remove_nodelist(list->owner, list); /* Destroy the list object */ - dom_document_alloc(list->owner, list, 0); + _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 @@ -175,19 +216,24 @@ dom_exception dom_nodelist_get_length(struct dom_nodelist *list, if (list->type == DOM_NODELIST_CHILDREN) { len++; } else if (list->type == DOM_NODELIST_BY_NAME) { - if (cur->name != NULL && - dom_string_cmp(cur->name, - list->data.name) == 0) { - len++; + /* Here, we compare two lwc_string pointer directly */ + if (list->data.n.any_name == true || ( + cur->name != NULL && + cur->name == list->data.n.name)) { + if (cur->type == DOM_ELEMENT_NODE) + len++; } } else { - if (cur->namespace != NULL && - dom_string_cmp(cur->namespace, - list->data.ns.namespace) == 0 && - cur->name != NULL && - dom_string_cmp(cur->name, - list->data.ns.localname) == 0) { - len++; + if (list->data.ns.any_namespace == true || + cur->namespace == + list->data.ns.namespace) { + if (list->data.ns.any_localname == true || + (cur->name != NULL && + cur->name == + list->data.ns.localname)) { + if (cur->type == DOM_ELEMENT_NODE) + len++; + } } } @@ -250,19 +296,24 @@ dom_exception _dom_nodelist_item(struct dom_nodelist *list, if (list->type == DOM_NODELIST_CHILDREN) { count++; } else if (list->type == DOM_NODELIST_BY_NAME) { - if (cur->name != NULL && - dom_string_cmp(cur->name, - list->data.name) == 0) { - count++; + if (list->data.n.any_name == true || ( + cur->name != NULL && + cur->name == list->data.n.name)) { + if (cur->type == DOM_ELEMENT_NODE) + count++; } } else { - if (cur->namespace != NULL && - dom_string_cmp(cur->namespace, - list->data.ns.namespace) == 0 && - cur->name != NULL && - dom_string_cmp(cur->name, - list->data.ns.localname) == 0) { - count++; + if (list->data.ns.any_namespace == true || + (cur->namespace != NULL && + cur->namespace == + list->data.ns.namespace)) { + if (list->data.ns.any_localname == true || + (cur->name != NULL && + cur->name == + list->data.ns.localname)) { + if (cur->type == DOM_ELEMENT_NODE) + count++; + } } } @@ -311,36 +362,49 @@ dom_exception _dom_nodelist_item(struct dom_nodelist *list, * Match a nodelist instance against a set of nodelist creation parameters * * \param list List to match + * \param type The type of the NodeList * \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_internal *root, struct dom_string *tagname, - struct dom_string *namespace, struct dom_string *localname) +bool _dom_nodelist_match(struct dom_nodelist *list, nodelist_type type, + struct dom_node_internal *root, struct lwc_string_s *tagname, + struct lwc_string_s *namespace, struct lwc_string_s *localname) { if (list->root != root) return false; - if (list->type == DOM_NODELIST_CHILDREN && tagname == NULL && - namespace == NULL && localname == NULL) { + if (list->type != type) + return false; + + if (list->type == DOM_NODELIST_CHILDREN) { 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_NAME) { + return (list->data.n.name == tagname); } - 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); + if (list->type == DOM_NODELIST_BY_NAMESPACE) { + return (list->data.ns.namespace == namespace) && + (list->data.ns.localname == localname); } return false; } + +/** + * Test whether the two NodeList are equal + * + * \param l1 One list + * \param l2 The other list + * \reutrn true for equal, false otherwise. + */ +bool _dom_nodelist_equal(struct dom_nodelist *l1, struct dom_nodelist *l2) +{ + return _dom_nodelist_match(l1, l1->type, l2->root, l2->data.n.name, + l2->data.ns.namespace, l2->data.ns.localname); +} + diff --git a/src/core/nodelist.h b/src/core/nodelist.h index 9629d03..e467aa6 100644 --- a/src/core/nodelist.h +++ b/src/core/nodelist.h @@ -16,16 +16,30 @@ struct dom_document; struct dom_node; struct dom_nodelist; struct dom_string; +struct lwc_string_s; + +/** + * The NodeList type + */ +typedef enum { + DOM_NODELIST_CHILDREN, + DOM_NODELIST_BY_NAME, + DOM_NODELIST_BY_NAMESPACE +} nodelist_type; /* Create a nodelist */ -dom_exception dom_nodelist_create(struct dom_document *doc, - struct dom_node_internal *root, struct dom_string *tagname, - struct dom_string *namespace, struct dom_string *localname, +dom_exception _dom_nodelist_create(struct dom_document *doc, nodelist_type type, + struct dom_node_internal *root, struct lwc_string_s *tagname, + struct lwc_string_s *namespace, struct lwc_string_s *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_internal *root, struct dom_string *tagname, - struct dom_string *namespace, struct dom_string *localname); +bool _dom_nodelist_match(struct dom_nodelist *list, nodelist_type type, + struct dom_node_internal *root, struct lwc_string_s *tagname, + struct lwc_string_s *namespace, struct lwc_string_s *localname); + +bool _dom_nodelist_equal(struct dom_nodelist *l1, struct dom_nodelist *l2); +#define dom_nodelist_equal(l1, l2) _dom_nodelist_equal( \ + (struct dom_nodelist *) (l1), (struct dom_nodelist *) (l2)) #endif diff --git a/src/core/pi.c b/src/core/pi.c index 754b362..634dc05 100644 --- a/src/core/pi.c +++ b/src/core/pi.c @@ -9,6 +9,8 @@ #include "core/node.h" #include "core/pi.h" +#include "utils/utils.h" + /** * A DOM processing instruction */ @@ -16,6 +18,13 @@ struct dom_processing_instruction { struct dom_node_internal base; /**< Base node */ }; +static struct dom_node_vtable pi_vtable = { + DOM_NODE_VTABLE +}; + +static struct dom_node_protect_vtable pi_protect_vtable = { + DOM_PI_PROTECT_VTABLE +}; /** * Create a processing instruction * @@ -30,25 +39,28 @@ struct dom_processing_instruction { * * The returned node will already be referenced. */ -dom_exception dom_processing_instruction_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *value, +dom_exception _dom_processing_instruction_create(struct dom_document *doc, + struct lwc_string_s *name, struct dom_string *value, struct dom_processing_instruction **result) { struct dom_processing_instruction *p; dom_exception err; /* Allocate the comment node */ - p = dom_document_alloc(doc, NULL, + p = _dom_document_alloc(doc, NULL, sizeof(struct dom_processing_instruction)); if (p == NULL) return DOM_NO_MEM_ERR; + + p->base.base.vtable = &pi_vtable; + p->base.vtable = &pi_protect_vtable; /* And initialise the node */ - err = dom_node_initialise(&p->base, doc, + err = _dom_processing_instruction_initialise(&p->base, doc, DOM_PROCESSING_INSTRUCTION_NODE, name, value, NULL, NULL); if (err != DOM_NO_ERR) { - dom_document_alloc(doc, p, 0); + _dom_document_alloc(doc, p, 0); return err; } @@ -65,12 +77,49 @@ dom_exception dom_processing_instruction_create(struct dom_document *doc, * * The contents of ::pi will be destroyed and ::pi will be freed. */ -void dom_processing_instruction_destroy(struct dom_document *doc, +void _dom_processing_instruction_destroy(struct dom_document *doc, struct dom_processing_instruction *pi) { /* Finalise base class */ - dom_node_finalise(doc, &pi->base); + _dom_processing_instruction_finalise(doc, &pi->base); /* Free processing instruction */ - dom_document_alloc(doc, pi, 0); + _dom_document_alloc(doc, pi, 0); +} + +/*-----------------------------------------------------------------------*/ + +/* Following comes the protected vtable */ + +/* The virtual destroy function of this class */ +void _dom_pi_destroy(struct dom_node_internal *node) +{ + _dom_processing_instruction_destroy(node->owner, + (struct dom_processing_instruction *) node); +} + +/* The memory allocator of this class */ +dom_exception _dom_pi_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + UNUSED(n); + struct dom_processing_instruction *a; + + a = _dom_document_alloc(doc, NULL, + sizeof(struct dom_processing_instruction)); + if (a == NULL) + return DOM_NO_MEM_ERR; + + *ret = (dom_node_internal *) a; + dom_node_set_owner(*ret, doc); + + return DOM_NO_ERR; } + +/* The copy constructor of this class */ +dom_exception _dom_pi_copy(struct dom_node_internal *new, + struct dom_node_internal *old) +{ + return _dom_node_copy(new, old); +} + diff --git a/src/core/pi.h b/src/core/pi.h index 4dca989..3f6d46c 100644 --- a/src/core/pi.h +++ b/src/core/pi.h @@ -13,12 +13,28 @@ struct dom_document; struct dom_processing_instruction; struct dom_string; +struct lwc_string_s; -dom_exception dom_processing_instruction_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *value, +dom_exception _dom_processing_instruction_create(struct dom_document *doc, + struct lwc_string_s *name, struct dom_string *value, struct dom_processing_instruction **result); -void dom_processing_instruction_destroy(struct dom_document *doc, +void _dom_processing_instruction_destroy(struct dom_document *doc, struct dom_processing_instruction *pi); +#define _dom_processing_instruction_initialise _dom_node_initialise +#define _dom_processing_instruction_finalise _dom_node_finalise + +/* Following comes the protected vtable */ +void _dom_pi_destroy(struct dom_node_internal *node); +dom_exception _dom_pi_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret); +dom_exception _dom_pi_copy(struct dom_node_internal *new, + struct dom_node_internal *old); + +#define DOM_PI_PROTECT_VTABLE \ + _dom_pi_destroy, \ + _dom_pi_alloc, \ + _dom_pi_copy + #endif diff --git a/src/core/string.c b/src/core/string.c index 806531a..e35f416 100644 --- a/src/core/string.c +++ b/src/core/string.c @@ -3,16 +3,17 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ +#include #include #include #include #include -#include - +#include "core/string.h" #include "core/document.h" #include "utils/utils.h" @@ -26,6 +27,10 @@ struct dom_string { size_t len; /**< Byte length of string */ + lwc_string *intern; /**< The lwc_string of this string */ + + lwc_context *context; /**< The lwc_context for the lwc_string */ + dom_alloc alloc; /**< Memory (de)allocation function */ void *pw; /**< Client-specific data */ @@ -40,6 +45,8 @@ static struct dom_string empty_string = { .refcnt = 1 }; + + /** * Claim a reference on a DOM string * @@ -60,8 +67,15 @@ void dom_string_ref(struct dom_string *str) */ void dom_string_unref(struct dom_string *str) { + if (str == NULL) + return; + if (--str->refcnt == 0) { - if (str->alloc != NULL) { + if (str->intern != NULL) { + lwc_context_unref(str->context); + lwc_context_string_unref(str->context, str->intern); + str->alloc(str, 0, str->pw); + } else if (str->alloc != NULL) { str->alloc(str->ptr, 0, str->pw); str->alloc(str, 0, str->pw); } @@ -71,11 +85,11 @@ void dom_string_unref(struct dom_string *str) /** * Create a DOM string from a string of characters * - * \param alloc Memory (de)allocation function - * \param pw Pointer to client-specific private data - * \param ptr Pointer to string of characters - * \param len Length, in bytes, of string of characters - * \param str Pointer to location to receive result + * \param alloc Memory (de)allocation function + * \param pw Pointer to client-specific private data + * \param ptr Pointer to string of characters + * \param len Length, in bytes, of string of characters + * \param str Pointer to location to receive result * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion * * The returned string will already be referenced, so there is no need @@ -89,7 +103,7 @@ dom_exception dom_string_create(dom_alloc alloc, void *pw, { struct dom_string *ret; - if (ptr == NULL && len == 0) { + if (ptr == NULL || len == 0) { dom_string_ref(&empty_string); *str = &empty_string; @@ -114,6 +128,9 @@ dom_exception dom_string_create(dom_alloc alloc, void *pw, ret->alloc = alloc; ret->pw = pw; + ret->intern = NULL; + ret->context = NULL; + ret->refcnt = 1; *str = ret; @@ -121,6 +138,152 @@ dom_exception dom_string_create(dom_alloc alloc, void *pw, return DOM_NO_ERR; } +/** + * Clone a dom_string if necessary. This method is used to create a new string + * with a new allocator, but if the allocator is the same with the paramter + * str, just ref the string. + * + * \param alloc The new allocator for this string + * \param pw The new pw for this string + * \param str The source dom_string + * \param ret The cloned dom_string + * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion + * + * @note: When both the alloc and pw are the same as the str's, we need no + * real clone, just ref the source string is ok. + */ +dom_exception dom_string_clone(dom_alloc alloc, void *pw, + struct dom_string *str, struct dom_string **ret) +{ + if (alloc == str->alloc && pw == str->pw) { + *ret = str; + dom_string_ref(str); + return DOM_NO_ERR; + } + + if (str->intern != NULL) { + return _dom_string_create_from_lwcstring(alloc, pw, + str->context, str->intern, ret); + } else { + return dom_string_create(alloc, pw, str->ptr, str->len, ret); + } +} + +/** + * Create a dom_string from a lwc_string + * + * \param ctx The lwc_context + * \param str The lwc_string + * \param ret The new dom_string + * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion + */ +dom_exception _dom_string_create_from_lwcstring(dom_alloc alloc, void *pw, + lwc_context *ctx, lwc_string *str, struct dom_string **ret) +{ + dom_string *r; + + if (str == NULL) { + *ret = NULL; + return DOM_NO_ERR; + } + + r = alloc(NULL, sizeof(struct dom_string), pw); + if (r == NULL) + return DOM_NO_MEM_ERR; + + if (str == NULL) { + *ret = &empty_string; + dom_string_ref(*ret); + return DOM_NO_ERR; + } + + r->context = ctx; + r->intern = str; + r->ptr = (uint8_t *)lwc_string_data(str); + r->len = lwc_string_length(str); + + r->alloc = alloc; + r->pw = pw; + + r->refcnt = 1; + + /* Ref the lwc_string */ + lwc_context_ref(ctx); + lwc_context_string_ref(ctx, str); + + *ret = r; + return DOM_NO_ERR; + +} + +/** + * Make the dom_string be interned in the lwc_context + * + * \param str The dom_string to be interned + * \param ctx The lwc_context to intern this dom_string + * \param lwcstr The result lwc_string + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_string_intern(struct dom_string *str, + struct lwc_context_s *ctx, struct lwc_string_s **lwcstr) +{ + lwc_string *ret; + lwc_error lerr; + + /* If this string is interned with the same context, do nothing */ + if (str->context != NULL && str->context == ctx) { + *lwcstr = str->intern; + return DOM_NO_ERR; + } + + lerr = lwc_context_intern(ctx, (const char *)str->ptr, str->len, &ret); + if (lerr != lwc_error_ok) { + return _dom_exception_from_lwc_error(lerr); + } + + if (str->context != NULL) { + lwc_context_unref(str->context); + lwc_context_string_unref(str->context, str->intern); + str->ptr = NULL; + } + + str->context = ctx; + str->intern = ret; + lwc_context_ref(ctx); + lwc_context_string_ref(ctx, ret); + + if (str->ptr != NULL) { + str->alloc(str->ptr, 0, str->pw); + } + + str->ptr = (uint8_t *) lwc_string_data(ret); + + *lwcstr = ret; + return DOM_NO_ERR; +} + +/** + * Get the internal lwc_string + * + * \param str The dom_string object + * \param ctx The lwc_context which intern this dom_string + * \param lwcstr The lwc_string of this dom-string + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception dom_string_get_intern(struct dom_string *str, + struct lwc_context_s **ctx, struct lwc_string_s **lwcstr) +{ + *ctx = str->context; + *lwcstr = str->intern; + + if (*ctx != NULL) + lwc_context_ref(*ctx); + if (*lwcstr != NULL) + lwc_context_string_ref(*ctx, *lwcstr); + + return DOM_NO_ERR; +} + /** * Case sensitively compare two DOM strings * @@ -132,12 +295,26 @@ dom_exception dom_string_create(dom_alloc alloc, void *pw, */ int dom_string_cmp(struct dom_string *s1, struct dom_string *s2) { + bool ret; + if (s1 == NULL) s1 = &empty_string; if (s2 == NULL) s2 = &empty_string; + if (s1->context == s2->context && s1->context != NULL) { + assert(s1->intern != NULL); + assert(s2->intern != NULL); + lwc_context_string_isequal(s1->context, s1->intern, + s2->intern, &ret); + if (ret == true) { + return 0; + } else { + return -1; + } + } + if (s1->len != s2->len) return 1; @@ -164,6 +341,19 @@ int dom_string_icmp(struct dom_string *s1, struct dom_string *s2) if (s2 == NULL) s2 = &empty_string; + bool ret; + if (s1->context == s2->context && s1->context != NULL) { + assert(s1->intern != NULL); + assert(s2->intern != NULL); + lwc_context_string_caseless_isequal(s1->context, s1->intern, + s2->intern, &ret); + if (ret == true) { + return 0; + } else { + return -1; + } + } + d1 = s1->ptr; d2 = s2->ptr; l1 = s1->len; @@ -304,6 +494,56 @@ uint32_t dom_string_length(struct dom_string *str) return clen; } +/** + * Get the UCS4 character at position index + * + * \param index The position of the charater + * \param ch The UCS4 character + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception dom_string_at(struct dom_string *str, uint32_t index, + uint32_t *ch) +{ + const uint8_t *s; + size_t clen, slen; + uint32_t c, i; + parserutils_error err; + + if (str == NULL) + str = &empty_string; + + s = str->ptr; + slen = str->len; + + i = 0; + + while (slen > 0) { + err = parserutils_charset_utf8_char_byte_length(s, &clen); + if (err != PARSERUTILS_OK) { + return (uint32_t) -1; + } + + i++; + if (i == index + 1) + break; + + s += clen; + slen -= clen; + } + + if (i == index + 1) { + err = parserutils_charset_utf8_to_ucs4(s, slen, &c, &clen); + if (err != PARSERUTILS_OK) { + return (uint32_t) -1; + } + + *ch = c; + return DOM_NO_ERR; + } else { + return DOM_DOMSTRING_SIZE_ERR; + } +} + /** * Concatenate two dom strings * @@ -322,16 +562,33 @@ dom_exception dom_string_concat(struct dom_string *s1, struct dom_string *s2, struct dom_string **result) { struct dom_string *concat; + dom_alloc alloc; + void *pw; + + assert(s1 != NULL); + assert(s2 != NULL); + + if (s1->alloc != NULL) { + alloc = s1->alloc; + pw = s1->pw; + } else if (s2->alloc != NULL) { + alloc = s2->alloc; + pw = s2->pw; + } else { + /* s1 == s2 == empty_string */ + *result = &empty_string; + return DOM_NO_ERR; + } - concat = s1->alloc(NULL, sizeof(struct dom_string), s1->pw); + concat = alloc(NULL, sizeof(struct dom_string), pw); if (concat == NULL) { return DOM_NO_MEM_ERR; } - concat->ptr = s1->alloc(NULL, s1->len + s2->len, s1->pw); + concat->ptr = alloc(NULL, s1->len + s2->len, pw); if (concat->ptr == NULL) { - s1->alloc(concat, 0, s1->pw); + alloc(concat, 0, pw); return DOM_NO_MEM_ERR; } @@ -342,8 +599,10 @@ dom_exception dom_string_concat(struct dom_string *s1, struct dom_string *s2, concat->len = s1->len + s2->len; - concat->alloc = s1->alloc; - concat->pw = s1->pw; + concat->alloc = alloc; + concat->pw = pw; + concat->context = NULL; + concat->intern = NULL; concat->refcnt = 1; @@ -382,7 +641,7 @@ dom_exception dom_string_substr(struct dom_string *str, /* Calculate the byte index of the start */ while (i1 > 0) { - err = parserutils_charset_utf8_next(s, slen - b1, b1, &b1); + err = parserutils_charset_utf8_next(s, slen, b1, &b1); if (err != PARSERUTILS_OK) { return DOM_NO_MEM_ERR; } @@ -395,7 +654,7 @@ dom_exception dom_string_substr(struct dom_string *str, /* Calculate the byte index of the end */ while (i2 > 0) { - err = parserutils_charset_utf8_next(s, slen - b2, b2, &b2); + err = parserutils_charset_utf8_next(s, slen, b2, &b2); if (err != PARSERUTILS_OK) { return DOM_NO_MEM_ERR; } @@ -451,7 +710,7 @@ dom_exception dom_string_insert(struct dom_string *target, ins = tlen; } else { while (offset > 0) { - err = parserutils_charset_utf8_next(t, tlen - ins, + err = parserutils_charset_utf8_next(t, tlen, ins, &ins); if (err != PARSERUTILS_OK) { @@ -492,6 +751,8 @@ dom_exception dom_string_insert(struct dom_string *target, res->alloc = target->alloc; res->pw = target->pw; + res->intern = NULL; + res->context = NULL; res->refcnt = 1; @@ -526,6 +787,9 @@ dom_exception dom_string_replace(struct dom_string *target, uint32_t b1, b2; parserutils_error err; + if (source == NULL) + source = &empty_string; + t = target->ptr; tlen = target->len; s = source->ptr; @@ -538,7 +802,7 @@ dom_exception dom_string_replace(struct dom_string *target, /* Calculate the byte index of the start */ while (i1 > 0) { - err = parserutils_charset_utf8_next(s, slen - b1, b1, &b1); + err = parserutils_charset_utf8_next(s, slen, b1, &b1); if (err != PARSERUTILS_OK) { return DOM_NO_MEM_ERR; @@ -552,7 +816,7 @@ dom_exception dom_string_replace(struct dom_string *target, /* Calculate the byte index of the end */ while (i2 > 0) { - err = parserutils_charset_utf8_next(s, slen - b2, b2, &b2); + err = parserutils_charset_utf8_next(s, slen, b2, &b2); if (err != PARSERUTILS_OK) { return DOM_NO_MEM_ERR; @@ -594,6 +858,8 @@ dom_exception dom_string_replace(struct dom_string *target, res->alloc = target->alloc; res->pw = target->pw; + res->intern = NULL; + res->context = NULL; res->refcnt = 1; @@ -618,8 +884,13 @@ dom_exception dom_string_replace(struct dom_string *target, dom_exception dom_string_dup(struct dom_string *str, struct dom_string **result) { - return dom_string_create(str->alloc, str->pw, str->ptr, str->len, - result); + if (str->intern != NULL) { + return _dom_string_create_from_lwcstring(str->alloc, str->pw, + str->context, str->intern, result); + } else { + return dom_string_create(str->alloc, str->pw, str->ptr, + str->len, result); + } } /** @@ -645,3 +916,48 @@ uint32_t dom_string_hash(struct dom_string *str) return hash; } +/** + * Convert a lwc_error to a dom_exception + * + * \param err The input lwc_error + * \return the dom_exception + */ +dom_exception _dom_exception_from_lwc_error(lwc_error err) +{ + switch (err) { + case lwc_error_ok: + return DOM_NO_ERR; + case lwc_error_oom: + return DOM_NO_MEM_ERR; + case lwc_error_range: + return DOM_INDEX_SIZE_ERR; + } + assert ("Unknow lwc_error, can't convert to dom_exception"); + /* Suppress compile errors */ + return DOM_NO_ERR; +} + +/** + * Compare the raw data of two lwc_strings for equality when the two strings + * belong to different lwc_context + * + * \param s1 The first lwc_string + * \param s2 The second lwc_string + * \return 0 for equal, non-zero otherwise + */ +int _dom_lwc_string_compare_raw(struct lwc_string_s *s1, + struct lwc_string_s *s2) +{ + const char *rs1, *rs2; + size_t len; + + if (lwc_string_length(s1) != lwc_string_length(s2)) + return -1; + + len = lwc_string_length(s1); + rs1 = lwc_string_data(s1); + rs2 = lwc_string_data(s2); + + return memcmp(rs1, rs2, len); +} + diff --git a/src/core/string.h b/src/core/string.h new file mode 100644 index 0000000..8372688 --- /dev/null +++ b/src/core/string.h @@ -0,0 +1,32 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2009 Bo Yang + */ + +#ifndef dom_internal_core_string_h_ +#define dom_internal_core_string_h_ + +#include + +/* Create a DOM string from a lwc_string + * This function call mainly used for create a string from lwc_string */ +dom_exception _dom_string_create_from_lwcstring(dom_alloc alloc, void *pw, + struct lwc_context_s *ctx, struct lwc_string_s *str, + struct dom_string **ret); + +/* Make the dom_string be interned in the lwc_context */ +dom_exception _dom_string_intern(struct dom_string *str, + struct lwc_context_s *ctx, struct lwc_string_s **lwcstr); + +/* Compare the raw data of two lwc_strings for equality when the two strings + * belong to different lwc_context */ +int _dom_lwc_string_compare_raw(struct lwc_string_s *s1, + struct lwc_string_s *s2); + +/* Map the lwc_error to dom_exception */ +dom_exception _dom_exception_from_lwc_error(lwc_error err); + +#endif + diff --git a/src/core/text.c b/src/core/text.c index bad20bf..6cb9e1f 100644 --- a/src/core/text.c +++ b/src/core/text.c @@ -3,18 +3,23 @@ * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ +#include + #include #include +#include + #include "core/characterdata.h" #include "core/document.h" #include "core/text.h" #include "utils/utils.h" /* The virtual table for dom_text */ -static struct dom_text_vtable text_vtable = { +struct dom_text_vtable text_vtable = { { { DOM_NODE_VTABLE @@ -24,15 +29,30 @@ static struct dom_text_vtable text_vtable = { DOM_TEXT_VTABLE }; -/* The destroy virtual function */ -void _dom_text_destroy(struct dom_node_internal *node); -void _dom_text_destroy(struct dom_node_internal *node) -{ - struct dom_document *doc; - dom_node_get_owner_document(node, &doc); +static struct dom_node_protect_vtable text_protect_vtable = { + DOM_TEXT_PROTECT_VTABLE +}; - dom_text_destroy(doc, (struct dom_text *) node); -} +/* Following comes helper functions */ +typedef enum walk_operation { + COLLECT, + DELETE +} walk_operation; +typedef enum walk_order { + LEFT, + RIGHT +} walk_order; + +/* Walk the logic-adjacent text in document order */ +static dom_exception walk_logic_adjacent_text_in_order( + dom_node_internal *node, walk_operation opt, + walk_order order, dom_string **ret, bool *cont); +/* Walk the logic-adjacent text */ +static dom_exception walk_logic_adjacent_text(dom_text *text, + walk_operation opt, dom_string **ret); + +/*----------------------------------------------------------------------*/ +/* Constructor and Destructor */ /** * Create a text node @@ -48,25 +68,29 @@ void _dom_text_destroy(struct dom_node_internal *node) * * The returned node will already be referenced. */ -dom_exception dom_text_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *value, +dom_exception _dom_text_create(struct dom_document *doc, + struct lwc_string_s *name, struct dom_string *value, struct dom_text **result) { struct dom_text *t; dom_exception err; /* Allocate the text node */ - t = dom_document_alloc(doc, NULL, sizeof(struct dom_text)); + t = _dom_document_alloc(doc, NULL, sizeof(struct dom_text)); if (t == NULL) return DOM_NO_MEM_ERR; /* And initialise the node */ - err = dom_text_initialise(t, doc, DOM_TEXT_NODE, name, value); + err = _dom_text_initialise(t, doc, DOM_TEXT_NODE, name, value); if (err != DOM_NO_ERR) { - dom_document_alloc(doc, t, 0); + _dom_document_alloc(doc, t, 0); return err; } + /* Compose the vtable */ + ((struct dom_node *) t)->vtable = &text_vtable; + ((struct dom_node_internal *) t)->vtable = &text_protect_vtable; + *result = t; return DOM_NO_ERR; @@ -80,13 +104,13 @@ dom_exception dom_text_create(struct dom_document *doc, * * The contents of ::text will be destroyed and ::text will be freed. */ -void dom_text_destroy(struct dom_document *doc, struct dom_text *text) +void _dom_text_destroy(struct dom_document *doc, struct dom_text *text) { /* Finalise node */ - dom_text_finalise(doc, text); + _dom_text_finalise(doc, text); /* Free node */ - dom_document_alloc(doc, text, 0); + _dom_document_alloc(doc, text, 0); } /** @@ -101,22 +125,18 @@ void dom_text_destroy(struct dom_document *doc, struct dom_text *text) * * ::doc, ::name and ::value will have their reference counts increased. */ -dom_exception dom_text_initialise(struct dom_text *text, +dom_exception _dom_text_initialise(struct dom_text *text, struct dom_document *doc, dom_node_type type, - struct dom_string *name, struct dom_string *value) + struct lwc_string_s *name, struct dom_string *value) { dom_exception err; /* Initialise the base class */ - err = dom_characterdata_initialise(&text->base, doc, type, + err = _dom_characterdata_initialise(&text->base, doc, type, name, value); if (err != DOM_NO_ERR) return err; - /* Compose the vtable */ - ((struct dom_node *) text)->vtable = &text_vtable; - text->base.base.destroy = &_dom_text_destroy; - /* Perform our type-specific initialisation */ text->element_content_whitespace = false; @@ -131,11 +151,14 @@ dom_exception dom_text_initialise(struct dom_text *text, * * The contents of ::text will be cleaned up. ::text will not be freed. */ -void dom_text_finalise(struct dom_document *doc, struct dom_text *text) +void _dom_text_finalise(struct dom_document *doc, struct dom_text *text) { - dom_characterdata_finalise(doc, &text->base); + _dom_characterdata_finalise(doc, &text->base); } +/*----------------------------------------------------------------------*/ +/* The public virtual functions */ + /** * Split a text node at a given character offset * @@ -181,7 +204,7 @@ dom_exception _dom_text_split_text(struct dom_text *text, } /* Create new node */ - err = dom_text_create(t->owner, t->name, value, &res); + err = _dom_text_create(t->owner, t->name, value, &res); if (err != DOM_NO_ERR) { dom_string_unref(value); return err; @@ -227,10 +250,7 @@ dom_exception _dom_text_get_is_element_content_whitespace( dom_exception _dom_text_get_whole_text(struct dom_text *text, struct dom_string **result) { - UNUSED(text); - UNUSED(result); - - return DOM_NOT_SUPPORTED_ERR; + return walk_logic_adjacent_text(text, COLLECT, result); } /** @@ -249,10 +269,240 @@ dom_exception _dom_text_get_whole_text(struct dom_text *text, dom_exception _dom_text_replace_whole_text(struct dom_text *text, struct dom_string *content, struct dom_text **result) { - UNUSED(text); - UNUSED(content); - UNUSED(result); + dom_exception err; + dom_string *ret; + + err = walk_logic_adjacent_text(text, DELETE, &ret); + if (err != DOM_NO_ERR) + return err; + + err = dom_characterdata_set_data(text, content); + if (err != DOM_NO_ERR) + return err; + + *result = text; + dom_node_ref(text); + + return DOM_NO_ERR; +} + +/*-----------------------------------------------------------------------*/ +/* The protected virtual functions */ + +/* The destroy function of this class */ +void __dom_text_destroy(struct dom_node_internal *node) +{ + struct dom_document *doc; + doc = dom_node_get_owner(node); + + _dom_text_destroy(doc, (struct dom_text *) node); +} + +/* The memory allocator for this class */ +dom_exception _dom_text_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret) +{ + UNUSED(n); + dom_text *a; + + a = _dom_document_alloc(doc, NULL, sizeof(struct dom_text)); + if (a == NULL) + return DOM_NO_MEM_ERR; + + *ret = (dom_node_internal *) a; + dom_node_set_owner(*ret, doc); + + return DOM_NO_ERR; +} + +/* The copy constructor of this class */ +dom_exception _dom_text_copy(struct dom_node_internal *new, + struct dom_node_internal *old) +{ + dom_text *ot = (dom_text *) old; + dom_text *nt = (dom_text *) new; - return DOM_NOT_SUPPORTED_ERR; + nt->element_content_whitespace = ot->element_content_whitespace; + + return _dom_characterdata_copy(new, old); +} + +/*----------------------------------------------------------------------*/ +/* Helper functions */ + +/** + * Walk the logic adjacent text in certain order + * + * \param node The start Text node + * \param opt The operation on each Text Node + * \param order The order + * \param ret The string of the logic adjacent text + * \param cont Whether the logic adjacent text is interrupt here + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception walk_logic_adjacent_text_in_order( + dom_node_internal *node, walk_operation opt, + walk_order order, dom_string **ret, bool *cont) +{ + dom_exception err; + dom_string *data, *tmp; + dom_node_internal *parent = dom_node_get_parent(node); + + /* If we reach the leaf of the DOM tree, just return to continue + * to next sibling of our parent */ + if (node == NULL) { + *cont = true; + return DOM_NO_ERR; + } + + while (node != NULL) { + /* If we reach the boundary of logical-adjacent text, we stop */ + if (node->type == DOM_ELEMENT_NODE || + node->type == DOM_COMMENT_NODE || + node->type == + DOM_PROCESSING_INSTRUCTION_NODE) { + *cont = false; + return DOM_NO_ERR; + } + + if (node->type == DOM_TEXT_NODE) { + /* According the DOM spec, text node never have child */ + assert(node->first_child == NULL); + assert(node->last_child == NULL); + if (opt == COLLECT) { + err = dom_characterdata_get_data(node, &data); + if (err == DOM_NO_ERR) + return err; + + tmp = *ret; + if (order == LEFT) { + err = dom_string_concat(data, tmp, ret); + if (err == DOM_NO_ERR) + return err; + } else if (order == RIGHT) { + err = dom_string_concat(tmp, data, ret); + if (err == DOM_NO_ERR) + return err; + } + + dom_string_unref(tmp); + dom_string_unref(data); + + *cont = true; + return DOM_NO_ERR; + } + + if (opt == DELETE) { + dom_node_internal *tn; + err = dom_node_remove_child(node->parent, + node, (void *) &tn); + if (err != DOM_NO_ERR) + return err; + + *cont = true; + dom_node_unref(tn); + return DOM_NO_ERR; + } + } + + dom_node_internal *p = dom_node_get_parent(node); + if (order == LEFT) { + if (node->last_child != NULL) { + node = node->last_child; + } else if (node->previous != NULL) { + node = node->previous; + } else { + while (p != parent && node == p->last_child) { + node = p; + p = dom_node_get_parent(p); + } + + node = node->previous; + } + } else { + if (node->first_child != NULL) { + node = node->first_child; + } else if (node->next != NULL) { + node = node->next; + } else { + while (p != parent && node == p->first_child) { + node = p; + p = dom_node_get_parent(p); + } + + node = node->next; + } + } + } + + return DOM_NO_ERR; +} + +/** + * Traverse the logic adjacent text. + * + * \param text The Text Node from which we start traversal + * \param opt The operation code + * \param ret The returned string if the opt is COLLECT + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception walk_logic_adjacent_text(dom_text *text, + walk_operation opt, dom_string **ret) +{ + dom_node_internal *node = (dom_node_internal *) text; + dom_node_internal *parent = node->parent; + dom_node_internal *left = node->previous; + dom_node_internal *right = node->next; + dom_exception err; + bool cont; + + if (parent->type == DOM_ENTITY_NODE) { + return DOM_NOT_SUPPORTED_ERR; + } + + /* Firstly, we look our left */ + err = walk_logic_adjacent_text_in_order(left, opt, LEFT, ret, &cont); + if (err != DOM_NO_ERR) { + dom_string_unref(*ret); + *ret = NULL; + return err; + } + + /* Ourself */ + if (opt == COLLECT) { + dom_string *data = NULL, *tmp = NULL; + err = dom_characterdata_get_data(text, &data); + if (err == DOM_NO_ERR) { + dom_string_unref(*ret); + return err; + } + + err = dom_string_concat(*ret, data, &tmp); + if (err == DOM_NO_ERR) { + dom_string_unref(*ret); + return err; + } + + dom_string_unref(*ret); + dom_string_unref(data); + *ret = tmp; + } else { + dom_node_internal *tn; + err = dom_node_remove_child(node->parent, node, + (void *) &tn); + if (err != DOM_NO_ERR) + return err; + dom_node_unref(tn); + } + + /* Now, look right */ + err = walk_logic_adjacent_text_in_order(right, opt, RIGHT, ret, &cont); + if (err != DOM_NO_ERR) { + dom_string_unref(*ret); + *ret = NULL; + return err; + } + + return DOM_NO_ERR; } diff --git a/src/core/text.h b/src/core/text.h index faf88cc..e5de56c 100644 --- a/src/core/text.h +++ b/src/core/text.h @@ -16,6 +16,8 @@ struct dom_document; struct dom_string; +struct lwc_context_s; +struct lwc_string_s; /** * A DOM text node @@ -27,7 +29,21 @@ struct dom_text { * content whitespace */ }; -/* Vitual functions for dom_text */ +/* Constructor and Destructor */ +dom_exception _dom_text_create(struct dom_document *doc, + struct lwc_string_s *name, struct dom_string *value, + struct dom_text **result); + +void _dom_text_destroy(struct dom_document *doc, struct dom_text *text); + +dom_exception _dom_text_initialise(struct dom_text *text, + struct dom_document *doc, dom_node_type type, + struct lwc_string_s *name, struct dom_string *value); + +void _dom_text_finalise(struct dom_document *doc, struct dom_text *text); + + +/* Virtual functions for dom_text */ dom_exception _dom_text_split_text(struct dom_text *text, unsigned long offset, struct dom_text **result); dom_exception _dom_text_get_is_element_content_whitespace( @@ -37,22 +53,26 @@ dom_exception _dom_text_get_whole_text(struct dom_text *text, dom_exception _dom_text_replace_whole_text(struct dom_text *text, struct dom_string *content, struct dom_text **result); -dom_exception dom_text_create(struct dom_document *doc, - struct dom_string *name, struct dom_string *value, - struct dom_text **result); - -void dom_text_destroy(struct dom_document *doc, struct dom_text *text); - -dom_exception dom_text_initialise(struct dom_text *text, - struct dom_document *doc, dom_node_type type, - struct dom_string *name, struct dom_string *value); - -void dom_text_finalise(struct dom_document *doc, struct dom_text *text); - #define DOM_TEXT_VTABLE \ _dom_text_split_text, \ _dom_text_get_is_element_content_whitespace, \ _dom_text_get_whole_text, \ _dom_text_replace_whole_text + +/* Following comes the protected vtable */ +void __dom_text_destroy(struct dom_node_internal *node); +dom_exception _dom_text_alloc(struct dom_document *doc, + struct dom_node_internal *n, struct dom_node_internal **ret); +dom_exception _dom_text_copy(struct dom_node_internal *new, + struct dom_node_internal *old); + +#define DOM_TEXT_PROTECT_VTABLE \ + __dom_text_destroy, \ + _dom_text_alloc, \ + _dom_text_copy + + +extern struct dom_text_vtable text_vtable; + #endif diff --git a/src/core/typeinfo.c b/src/core/typeinfo.c new file mode 100644 index 0000000..4ebefcd --- /dev/null +++ b/src/core/typeinfo.c @@ -0,0 +1,80 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2009 Bo Yang + */ + +#include +#include + +#include "utils/utils.h" + +/* TypeInfo object */ +struct dom_type_info { + struct lwc_string_s *type; /**< Type name */ + struct lwc_string_s *namespace; /**< Type namespace */ +}; + +/** + * Get the type name of this dom_type_info + * + * \param ti The dom_type_info + * \param ret The name + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + * + * We don't support this API now, so this function call always + * return DOM_NOT_SUPPORTED_ERR. + */ +dom_exception _dom_type_info_get_type_name(dom_type_info *ti, + struct dom_string **ret) +{ + UNUSED(ti); + UNUSED(ret); + + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Get the namespace of this type info + * + * \param ti The dom_type_info + * \param ret The namespace + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + * + * We don't support this API now, so this function call always + * return DOM_NOT_SUPPORTED_ERR. + */ +dom_exception _dom_type_info_get_type_namespace(dom_type_info *ti, + struct dom_string **ret) +{ + UNUSED(ti); + UNUSED(ret); + return DOM_NOT_SUPPORTED_ERR; +} + +/** + * Whether this type info is derived from another one + * + * \param ti The dom_type_info + * \param namespace The namespace of name + * \param name The name of the base typeinfo + * \param method The deriving method + * \param ret The return value + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + * + * We don't support this API now, so this function call always + * return DOM_NOT_SUPPORTED_ERR. + */ +dom_exception _dom_type_info_is_derived(dom_type_info *ti, + struct dom_string *namespace, struct dom_string *name, + dom_type_info_derivation_method method, bool *ret) +{ + UNUSED(ti); + UNUSED(namespace); + UNUSED(name); + UNUSED(method); + UNUSED(ret); + return DOM_NOT_SUPPORTED_ERR; +} + diff --git a/src/utils/Makefile b/src/utils/Makefile index c80f261..428a9cf 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -1,4 +1,5 @@ # Sources -DIR_SOURCES := namespace.c +DIR_SOURCES := namespace.c hashtable.c resource_mgr.c character_valid.c \ + validate.c include build/makefiles/Makefile.subdir diff --git a/src/utils/character_valid.c b/src/utils/character_valid.c new file mode 100644 index 0000000..2251075 --- /dev/null +++ b/src/utils/character_valid.c @@ -0,0 +1,217 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2009 Bo Yang + */ + +#include "utils/character_valid.h" + +#include + +static const struct xml_char_range base_char_range[] = { {0x41, 0x5a}, + {0x61, 0x7a}, {0xc0, 0xd6}, {0xd8, 0xf6}, {0x00f8, 0x00ff}, + {0x100, 0x131}, {0x134, 0x13e}, {0x141, 0x148}, {0x14a, 0x17e}, + {0x180, 0x1c3}, {0x1cd, 0x1f0}, {0x1f4, 0x1f5}, {0x1fa, 0x217}, + {0x250, 0x2a8}, {0x2bb, 0x2c1}, {0x386, 0x386}, {0x388, 0x38a}, + {0x38c, 0x38c}, {0x38e, 0x3a1}, {0x3a3, 0x3ce}, {0x3d0, 0x3d6}, + {0x3da, 0x3da}, {0x3dc, 0x3dc}, {0x3de, 0x3de}, {0x3e0, 0x3e0}, + {0x3e2, 0x3f3}, {0x401, 0x40c}, {0x40e, 0x44f}, {0x451, 0x45c}, + {0x45e, 0x481}, {0x490, 0x4c4}, {0x4c7, 0x4c8}, {0x4cb, 0x4cc}, + {0x4d0, 0x4eb}, {0x4ee, 0x4f5}, {0x4f8, 0x4f9}, {0x531, 0x556}, + {0x559, 0x559}, {0x561, 0x586}, {0x5d0, 0x5ea}, {0x5f0, 0x5f2}, + {0x621, 0x63a}, {0x641, 0x64a}, {0x671, 0x6b7}, {0x6ba, 0x6be}, + {0x6c0, 0x6ce}, {0x6d0, 0x6d3}, {0x6d5, 0x6d5}, {0x6e5, 0x6e6}, + {0x905, 0x939}, {0x93d, 0x93d}, {0x958, 0x961}, {0x985, 0x98c}, + {0x98f, 0x990}, {0x993, 0x9a8}, {0x9aa, 0x9b0}, {0x9b2, 0x9b2}, + {0x9b6, 0x9b9}, {0x9dc, 0x9dd}, {0x9df, 0x9e1}, {0x9f0, 0x9f1}, + {0xa05, 0xa0a}, {0xa0f, 0xa10}, {0xa13, 0xa28}, {0xa2a, 0xa30}, + {0xa32, 0xa33}, {0xa35, 0xa36}, {0xa38, 0xa39}, {0xa59, 0xa5c}, + {0xa5e, 0xa5e}, {0xa72, 0xa74}, {0xa85, 0xa8b}, {0xa8d, 0xa8d}, + {0xa8f, 0xa91}, {0xa93, 0xaa8}, {0xaaa, 0xab0}, {0xab2, 0xab3}, + {0xab5, 0xab9}, {0xabd, 0xabd}, {0xae0, 0xae0}, {0xb05, 0xb0c}, + {0xb0f, 0xb10}, {0xb13, 0xb28}, {0xb2a, 0xb30}, {0xb32, 0xb33}, + {0xb36, 0xb39}, {0xb3d, 0xb3d}, {0xb5c, 0xb5d}, {0xb5f, 0xb61}, + {0xb85, 0xb8a}, {0xb8e, 0xb90}, {0xb92, 0xb95}, {0xb99, 0xb9a}, + {0xb9c, 0xb9c}, {0xb9e, 0xb9f}, {0xba3, 0xba4}, {0xba8, 0xbaa}, + {0xbae, 0xbb5}, {0xbb7, 0xbb9}, {0xc05, 0xc0c}, {0xc0e, 0xc10}, + {0xc12, 0xc28}, {0xc2a, 0xc33}, {0xc35, 0xc39}, {0xc60, 0xc61}, + {0xc85, 0xc8c}, {0xc8e, 0xc90}, {0xc92, 0xca8}, {0xcaa, 0xcb3}, + {0xcb5, 0xcb9}, {0xcde, 0xcde}, {0xce0, 0xce1}, {0xd05, 0xd0c}, + {0xd0e, 0xd10}, {0xd12, 0xd28}, {0xd2a, 0xd39}, {0xd60, 0xd61}, + {0xe01, 0xe2e}, {0xe30, 0xe30}, {0xe32, 0xe33}, {0xe40, 0xe45}, + {0xe81, 0xe82}, {0xe84, 0xe84}, {0xe87, 0xe88}, {0xe8a, 0xe8a}, + {0xe8d, 0xe8d}, {0xe94, 0xe97}, {0xe99, 0xe9f}, {0xea1, 0xea3}, + {0xea5, 0xea5}, {0xea7, 0xea7}, {0xeaa, 0xeab}, {0xead, 0xeae}, + {0xeb0, 0xeb0}, {0xeb2, 0xeb3}, {0xebd, 0xebd}, {0xec0, 0xec4}, + {0xf40, 0xf47}, {0xf49, 0xf69}, {0x10a0, 0x10c5}, {0x10d0, 0x10f6}, + {0x1100, 0x1100}, {0x1102, 0x1103}, {0x1105, 0x1107}, {0x1109, 0x1109}, + {0x110b, 0x110c}, {0x110e, 0x1112}, {0x113c, 0x113c}, {0x113e, 0x113e}, + {0x1140, 0x1140}, {0x114c, 0x114c}, {0x114e, 0x114e}, {0x1150, 0x1150}, + {0x1154, 0x1155}, {0x1159, 0x1159}, {0x115f, 0x1161}, {0x1163, 0x1163}, + {0x1165, 0x1165}, {0x1167, 0x1167}, {0x1169, 0x1169}, {0x116d, 0x116e}, + {0x1172, 0x1173}, {0x1175, 0x1175}, {0x119e, 0x119e}, {0x11a8, 0x11a8}, + {0x11ab, 0x11ab}, {0x11ae, 0x11af}, {0x11b7, 0x11b8}, {0x11ba, 0x11ba}, + {0x11bc, 0x11c2}, {0x11eb, 0x11eb}, {0x11f0, 0x11f0}, {0x11f9, 0x11f9}, + {0x1e00, 0x1e9b}, {0x1ea0, 0x1ef9}, {0x1f00, 0x1f15}, {0x1f18, 0x1f1d}, + {0x1f20, 0x1f45}, {0x1f48, 0x1f4d}, {0x1f50, 0x1f57}, {0x1f59, 0x1f59}, + {0x1f5b, 0x1f5b}, {0x1f5d, 0x1f5d}, {0x1f5f, 0x1f7d}, {0x1f80, 0x1fb4}, + {0x1fb6, 0x1fbc}, {0x1fbe, 0x1fbe}, {0x1fc2, 0x1fc4}, {0x1fc6, 0x1fcc}, + {0x1fd0, 0x1fd3}, {0x1fd6, 0x1fdb}, {0x1fe0, 0x1fec}, {0x1ff2, 0x1ff4}, + {0x1ff6, 0x1ffc}, {0x2126, 0x2126}, {0x212a, 0x212b}, {0x212e, 0x212e}, + {0x2180, 0x2182}, {0x3041, 0x3094}, {0x30a1, 0x30fa}, {0x3105, 0x312c}, + {0xac00, 0xd7a3} +}; + +const struct xml_char_group base_char_group = { + sizeof(base_char_range) / sizeof(base_char_range[0]), + base_char_range}; + +static const struct xml_char_range char_range[] = { {0x100, 0xd7ff}, + {0xe000, 0xfffd}, {0x10000, 0x10ffff} +}; + +const struct xml_char_group char_group = { + sizeof(char_range) / sizeof(char_range[0]), char_range}; + +static const struct xml_char_range combining_char_range[] = { {0x300, 0x345}, + {0x360, 0x361}, {0x483, 0x486}, {0x591, 0x5a1}, {0x5a3, 0x5b9}, + {0x5bb, 0x5bd}, {0x5bf, 0x5bf}, {0x5c1, 0x5c2}, {0x5c4, 0x5c4}, + {0x64b, 0x652}, {0x670, 0x670}, {0x6d6, 0x6dc}, {0x6dd, 0x6df}, + {0x6e0, 0x6e4}, {0x6e7, 0x6e8}, {0x6ea, 0x6ed}, {0x901, 0x903}, + {0x93c, 0x93c}, {0x93e, 0x94c}, {0x94d, 0x94d}, {0x951, 0x954}, + {0x962, 0x963}, {0x981, 0x983}, {0x9bc, 0x9bc}, {0x9be, 0x9be}, + {0x9bf, 0x9bf}, {0x9c0, 0x9c4}, {0x9c7, 0x9c8}, {0x9cb, 0x9cd}, + {0x9d7, 0x9d7}, {0x9e2, 0x9e3}, {0xa02, 0xa02}, {0xa3c, 0xa3c}, + {0xa3e, 0xa3e}, {0xa3f, 0xa3f}, {0xa40, 0xa42}, {0xa47, 0xa48}, + {0xa4b, 0xa4d}, {0xa70, 0xa71}, {0xa81, 0xa83}, {0xabc, 0xabc}, + {0xabe, 0xac5}, {0xac7, 0xac9}, {0xacb, 0xacd}, {0xb01, 0xb03}, + {0xb3c, 0xb3c}, {0xb3e, 0xb43}, {0xb47, 0xb48}, {0xb4b, 0xb4d}, + {0xb56, 0xb57}, {0xb82, 0xb83}, {0xbbe, 0xbc2}, {0xbc6, 0xbc8}, + {0xbca, 0xbcd}, {0xbd7, 0xbd7}, {0xc01, 0xc03}, {0xc3e, 0xc44}, + {0xc46, 0xc48}, {0xc4a, 0xc4d}, {0xc55, 0xc56}, {0xc82, 0xc83}, + {0xcbe, 0xcc4}, {0xcc6, 0xcc8}, {0xcca, 0xccd}, {0xcd5, 0xcd6}, + {0xd02, 0xd03}, {0xd3e, 0xd43}, {0xd46, 0xd48}, {0xd4a, 0xd4d}, + {0xd57, 0xd57}, {0xe31, 0xe31}, {0xe34, 0xe3a}, {0xe47, 0xe4e}, + {0xeb1, 0xeb1}, {0xeb4, 0xeb9}, {0xebb, 0xebc}, {0xec8, 0xecd}, + {0xf18, 0xf19}, {0xf35, 0xf35}, {0xf37, 0xf37}, {0xf39, 0xf39}, + {0xf3e, 0xf3e}, {0xf3f, 0xf3f}, {0xf71, 0xf84}, {0xf86, 0xf8b}, + {0xf90, 0xf95}, {0xf97, 0xf97}, {0xf99, 0xfad}, {0xfb1, 0xfb7}, + {0xfb9, 0xfb9}, {0x20d0, 0x20dc}, {0x20e1, 0x20e1}, {0x302a, 0x302f}, + {0x3099, 0x3099}, {0x309a, 0x309a} +}; + +const struct xml_char_group combining_char_group = { + sizeof(combining_char_range) / sizeof(combining_char_range[0]), + combining_char_range }; + +static const struct xml_char_range digit_char_range[] = { {0x30, 0x39}, + {0x660, 0x669}, {0x6f0, 0x6f9}, {0x966, 0x96f}, {0x9e6, 0x9ef}, + {0xa66, 0xa6f}, {0xae6, 0xaef}, {0xb66, 0xb6f}, {0xbe7, 0xbef}, + {0xc66, 0xc6f}, {0xce6, 0xcef}, {0xd66, 0xd6f}, {0xe50, 0xe59}, + {0xed0, 0xed9}, {0xf20, 0xf29} +}; + +const struct xml_char_group digit_char_group = { + sizeof(digit_char_range) / sizeof(digit_char_range[0]), + digit_char_range }; + +static const struct xml_char_range extender_range[] = { {0xb7, 0xb7}, + {0x2d0, 0x2d0}, {0x2d1, 0x2d1}, {0x387, 0x387}, {0x640, 0x640}, + {0xe46, 0xe46}, {0xec6, 0xec6}, {0x3005, 0x3005}, {0x3031, 0x3035}, + {0x309d, 0x309e}, {0x30fc, 0x30fe} +}; + +const struct xml_char_group extender_group = { + sizeof(extender_range) / sizeof(extender_range[0]), + extender_range }; + +static const struct xml_char_range ideographic_range[] = { {0x3007, 0x3007}, + {0x3021, 0x3029}, {0x4e00, 0x9fa5} +}; + +const struct xml_char_group ideographic_group = { + sizeof(ideographic_range) / sizeof(ideographic_range[0]), + ideographic_range }; + +/* The binary search helper function */ +static bool binary_search(unsigned int ch, int left, int right, + const struct xml_char_range *range); + +/* Search for ch in range[left, right] */ +bool binary_search(unsigned int ch, int left, int right, + const struct xml_char_range *range) +{ + if (left > right) + return false; + + int mid = (left + right) / 2; + if (ch >= range[mid].start && ch <= range[mid].end) + return true; + + if (ch < range[mid].start) + return binary_search(ch, left, mid - 1, range); + + if (ch > range[mid].end) + return binary_search(ch, mid + 1, right, range); + + return false; +} + +/** + * Test whether certain character belongs to some XML character group + * + * \param ch The character being tested + * \param group The character group + * \return true if the character belongs to the group, false otherwise. + * + * Generally, we use an algorithm like binary search to find the desired + * character in the group. The time complexity is about lg(n) and here n is + * at most 180, so, I think the algorithm is fast enough for name validation. + */ +bool _dom_is_character_in_group(unsigned int ch, + const struct xml_char_group *group) +{ + int len = group->len; + const struct xml_char_range *range = group->range; + + if (ch < range[0].start || ch > range[len-1].end) + return false; + + return binary_search(ch, 0, len - 1, range); +} + +#ifdef CHVALID_DEBUG +/* The following is the testcases for this file. + * Compile this file : + * + * gcc -o test -DCHVALID_DEBUG character_valid.c + * + */ +#include + +int main(int argc, char **argv) +{ + unsigned int ch = 0x666; + + assert(is_digit(ch) == true); + assert(is_base_char(ch) == false); + assert(is_char(ch) == true); + assert(is_extender(ch) == false); + assert(is_combining_char(ch) == false); + assert(is_ideographic(ch) == false); + + ch = 0xf40; + + assert(is_digit(ch) == false); + assert(is_base_char(ch) == true); + assert(is_char(ch) == true); + assert(is_extender(ch) == false); + assert(is_combining_char(ch) == false); + assert(is_ideographic(ch) == false); + + printf("The test pass.\n"); + return 0; +} + +#endif diff --git a/src/utils/character_valid.h b/src/utils/character_valid.h new file mode 100644 index 0000000..5094e7c --- /dev/null +++ b/src/utils/character_valid.h @@ -0,0 +1,54 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2009 Bo Yang + * + * This file contains the API used to validate whether certain character in + * name/value is legal according the XML 1.0 standard. See + * + * http://www.w3.org/TR/2004/REC-xml-20040204/ + * http://www.w3.org/TR/REC-xml/ + * + * for detail. + */ + +#ifndef dom_utils_character_valid_h_ +#define dom_utils_character_valid_h_ + +#include +#include + +struct xml_char_range { + unsigned int start; + unsigned int end; +}; + +struct xml_char_group { + size_t len; + const struct xml_char_range *range; +}; + +/* The groups */ +extern const struct xml_char_group base_char_group; +extern const struct xml_char_group char_group; +extern const struct xml_char_group combining_char_group; +extern const struct xml_char_group digit_char_group; +extern const struct xml_char_group extender_group; +extern const struct xml_char_group ideographic_group; + +bool _dom_is_character_in_group(unsigned int ch, + const struct xml_char_group *group); + +#define is_base_char(ch) _dom_is_character_in_group((ch), &base_char_group) +#define is_char(ch) _dom_is_character_in_group((ch), &char_group) +#define is_combining_char(ch) _dom_is_character_in_group((ch), \ + &combining_char_group) +#define is_digit(ch) _dom_is_character_in_group((ch), &digit_char_group) +#define is_extender(ch) _dom_is_character_in_group((ch), &extender_group) +#define is_ideographic(ch) _dom_is_character_in_group((ch), &ideographic_group) + +#define is_letter(ch) (is_base_char(ch) || is_ideographic(ch)) + +#endif + diff --git a/src/utils/hashtable.c b/src/utils/hashtable.c new file mode 100644 index 0000000..c2ff8ce --- /dev/null +++ b/src/utils/hashtable.c @@ -0,0 +1,492 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2006 Rob Kendrick + * Copyright 2006 Richard Wilson + * Copyright 2009 Bo Yang + */ + +#include +#include +#include +#include +#ifdef TEST_RIG +#include +#endif +#include "utils/hashtable.h" + +/* The hash table entry */ +struct _dom_hash_entry { + void *key; /**< The key pointer */ + void *value; /**< The value pointer */ + struct _dom_hash_entry *next; /**< Next entry */ +}; + +/* The hash table */ +struct dom_hash_table { + unsigned int nchains; /**< The chains number */ + dom_hash_func hash; /**< The hash function */ + struct _dom_hash_entry **chain; /**< The chain head */ + unsigned int number; /**< The enries in this table */ + + dom_alloc alloc; /**< Memory allocation function */ + void *ptr; /**< The private data for the memory allocator */ +}; + + +/** + * Create a new hash table, and return a context for it. The memory consumption + * of a hash table is approximately 8 + (nchains * 12) bytes if it is empty. + * + * \param chains Number of chains/buckets this hash table will have. This + * should be a prime number, and ideally a prime number just + * over a power of two, for best performance and distribution + * \param hash The hash function + * \param alloc The memory allocator + * \param ptr The private pointer for the allocator + * \return struct dom_hash_table containing the context of this hash table or + * NULL if there is insufficent memory to create it and its chains. + */ +struct dom_hash_table *_dom_hash_create(unsigned int chains, dom_hash_func hash, + dom_alloc alloc, void *ptr) +{ + struct dom_hash_table *r = alloc(NULL, sizeof(struct dom_hash_table), + ptr); + + if (r == NULL) { + return NULL; + } + + r->nchains = chains; + r->hash = hash; + r->alloc = alloc; + r->ptr = ptr; + r->chain = (struct _dom_hash_entry **)alloc(NULL, + chains*sizeof(struct _dom_hash_entry *), ptr); + r->number = 0; + + unsigned int i; + for (i = 0; i < chains; i++) + r->chain[i] = NULL; + + if (r->chain == NULL) { + alloc(r, 0, ptr); + return NULL; + } + + return r; +} + +/** + * Clone a hash table. + * + * \param ht Hash table to clone. + * \param alloc The allocator. + * \param pw The private data for the allocator. + * \param kf The function pointer used to copy the key. + * \param key_pw The private data for the key cloner. + * \param vf The function pointer used to copy the value. + * \param value_pw The private data for the value cloner. + * + * \return The cloned hash table. + */ +struct dom_hash_table *_dom_hash_clone(struct dom_hash_table *ht, + dom_alloc alloc, void *pw, dom_key_func kf, void *key_pw, + dom_value_func vf, void *value_pw) +{ + struct dom_hash_table *ret; + + ret = _dom_hash_create(ht->nchains, ht->hash, alloc, pw); + if (ret == NULL) + return NULL; + + void *key = NULL, *nkey = NULL; + void *value = NULL, *nvalue = NULL; + unsigned int c1, *c2 = NULL; + while ( (key = _dom_hash_iterate(ht, &c1, &c2)) != NULL) { + nkey = kf(key, key_pw, alloc, pw, true); + if (nkey == NULL) { + _dom_hash_destroy(ret, kf, key_pw, vf, value_pw); + return NULL; + } + + value = _dom_hash_get(ht, key); + nvalue = vf(value, value_pw, alloc, pw, true); + if (nvalue == NULL) { + kf(nkey, key_pw, alloc, pw, false); + _dom_hash_destroy(ret, kf, key_pw, vf, value_pw); + return NULL; + } + + if (_dom_hash_add(ret, nkey, nvalue, false) == false) { + _dom_hash_destroy(ret, kf, key_pw, vf, value_pw); + return NULL; + } + } + + return ret; +} + +/** + * Destroys a hash table, freeing all memory associated with it. + * + * \param ht Hash table to destroy. After the function returns, this + * will nolonger be valid + * \param kf The key destroy function + * \param key_pw The key destroy function private data + * \param vf The value destroy function + * \param value_pw The value destroy function private data + */ +void _dom_hash_destroy(struct dom_hash_table *ht, dom_key_func kf, + void *key_pw, dom_value_func vf, void *value_pw) +{ + unsigned int i; + + if (ht == NULL) + return; + + assert(ht->alloc != NULL); + + for (i = 0; i < ht->nchains; i++) { + if (ht->chain[i] != NULL) { + struct _dom_hash_entry *e = ht->chain[i]; + while (e) { + struct _dom_hash_entry *n = e->next; + if (kf != NULL) { + kf(e->key, key_pw, ht->alloc, + ht->ptr, false); + } + if (vf != NULL) { + vf(e->value, value_pw, ht->alloc, + ht->ptr, false); + } + ht->alloc(e, 0, ht->ptr); + e = n; + } + } + } + + ht->alloc(ht->chain, 0, ht->ptr); + ht->alloc(ht, 0, ht->ptr); +} + +/** + * Adds a key/value pair to a hash table + * + * \param ht The hash table context to add the key/value pair to. + * \param key The key to associate the value with. + * \param value The value to associate the key with. + * \return true if the add succeeded, false otherwise. (Failure most likely + * indicates insufficent memory to make copies of the key and value. + */ +bool _dom_hash_add(struct dom_hash_table *ht, void *key, void *value, + bool replace) +{ + unsigned int h, c; + struct _dom_hash_entry *e; + + if (ht == NULL || key == NULL || value == NULL) + return false; + + h = ht->hash(key); + c = h % ht->nchains; + + for (e = ht->chain[c]; e; e = e->next) + if (key == e->key) { + if (replace == true) { + e->value = value; + return true; + } else { + return false; + } + } + + assert(ht->alloc != NULL); + + e = ht->alloc(NULL, sizeof(struct _dom_hash_entry), ht->ptr); + if (e == NULL) { + return false; + } + + e->key = key; + e->value = value; + + e->next = ht->chain[c]; + ht->chain[c] = e; + ht->number ++; + + return true; +} + +/** + * Looks up a the value associated with with a key from a specific hash table. + * + * \param ht The hash table context to look up + * \param key The key to search for + * \return The value associated with the key, or NULL if it was not found. + */ +void *_dom_hash_get(struct dom_hash_table *ht, void *key) +{ + unsigned int h, c; + struct _dom_hash_entry *e; + + if (ht == NULL || key == NULL) + return NULL; + + h = ht->hash(key); + c = h % ht->nchains; + + for (e = ht->chain[c]; e; e = e->next) + if (key == e->key) + return e->value; + + return NULL; +} + +/** + * Delete the key from the hashtable. + * + * \param ht The hashtable object + * \param key The key to delete + * \return The deleted value + */ +void *_dom_hash_del(struct dom_hash_table *ht, void *key) +{ + unsigned int h, c; + struct _dom_hash_entry *e, *p; + void *ret; + + if (ht == NULL || key == NULL) + return NULL; + + h = ht->hash(key); + c = h % ht->nchains; + + assert(ht->alloc != NULL); + + p = ht->chain[c]; + for (e = p; e; p = e, e = e->next) + if (key == e->key) { + if (p != e) { + p->next = e->next; + } else { + /* The first item in this chain is target*/ + ht->chain[c] = e->next; + } + + ret = e->value; + ht->alloc(e, 0, ht->ptr); + ht->number --; + return ret; + } + + return NULL; +} + +/** + * Iterate through all available hash keys. + * + * \param ht The hash table context to iterate. + * \param c1 Pointer to first context + * \param c2 Pointer to second context (set to 0 on first call) + * \return The next hash key, or NULL for no more keys + */ +void *_dom_hash_iterate(struct dom_hash_table *ht, unsigned int *c1, + unsigned int **c2) +{ + struct _dom_hash_entry **he = (struct _dom_hash_entry **)c2; + + if (ht == NULL) + return NULL; + + if (!*he) + *c1 = -1; + else + *he = (*he)->next; + + if (*he) + return (*he)->key; + + while (!*he) { + (*c1)++; + if (*c1 >= ht->nchains) + return NULL; + *he = ht->chain[*c1]; + } + return (*he)->key; +} + +/** + * Get the number of elements in this hash table + * + * \param ht The hash table + * + * \return the number of elements + */ +unsigned int _dom_hash_get_length(struct dom_hash_table *ht) +{ + return ht->number; +} + +/** + * Get the chain number of this hash table + * + * \param ht The hash table + * + * \return the number of chains + */ +unsigned int _dom_hash_get_chains(struct dom_hash_table *ht) +{ + return ht->nchains; +} + +/** + * Get the hash function of this hash table + * + * \param ht The hash table + * + * \return the hash function + */ +dom_hash_func _dom_hash_get_func(struct dom_hash_table *ht) +{ + return ht->hash; +} + +/* A simple test rig. To compile, use: + * gcc -g -o hashtest -I../ -I../../include -DTEST_RIG hashtable.c + * + * If you make changes to this hash table implementation, please rerun this + * test, and if possible, through valgrind to make sure there are no memory + * leaks or invalid memory accesses. If you add new functionality, please + * include a test for it that has good coverage along side the other tests. + */ + +#ifdef TEST_RIG + + +/** + * Hash a pointer, returning a 32bit value. + * + * \param ptr The pointer to hash. + * \return the calculated hash value for the pointer. + */ + +static inline unsigned int _dom_hash_pointer_fnv(void *ptr) +{ + return (unsigned int) ptr; +} + +static void *test_alloc(void *p, size_t size, void *ptr) +{ + if (p != NULL) { + free(p); + return NULL; + } + + if (p == NULL) { + return malloc(size); + } +} + +int main(int argc, char *argv[]) +{ + struct dom_hash_table *a, *b; + FILE *dict; + char keybuf[BUFSIZ], valbuf[BUFSIZ]; + int i; + char *cow="cow", *moo="moo", *pig="pig", *oink="oink", + *chicken="chikcken", *cluck="cluck", + *dog="dog", *woof="woof", *cat="cat", + *meow="meow"; + void *ret; + + a = _dom_hash_create(79, _dom_hash_pointer_fnv, test_alloc, NULL); + assert(a != NULL); + + b = _dom_hash_create(103, _dom_hash_pointer_fnv, test_alloc, NULL); + assert(b != NULL); + + _dom_hash_add(a, cow, moo ,true); + _dom_hash_add(b, moo, cow ,true); + + _dom_hash_add(a, pig, oink ,true); + _dom_hash_add(b, oink, pig ,true); + + _dom_hash_add(a, chicken, cluck ,true); + _dom_hash_add(b, cluck, chicken ,true); + + _dom_hash_add(a, dog, woof ,true); + _dom_hash_add(b, woof, dog ,true); + + _dom_hash_add(a, cat, meow ,true); + _dom_hash_add(b, meow, cat ,true); + +#define MATCH(x,y) assert(!strcmp((char *)hash_get(a, x), (char *)y)); \ + assert(!strcmp((char *)hash_get(b, y), (char *)x)) + MATCH(cow, moo); + MATCH(pig, oink); + MATCH(chicken, cluck); + MATCH(dog, woof); + MATCH(cat, meow); + + assert(hash_get_length(a) == 5); + assert(hash_get_length(b) == 5); + + _dom_hash_del(a, cat); + _dom_hash_del(b, meow); + assert(hash_get(a, cat) == NULL); + assert(hash_get(b, meow) == NULL); + + assert(hash_get_length(a) == 4); + assert(hash_get_length(b) == 4); + + _dom_hash_destroy(a, NULL, NULL); + _dom_hash_destroy(b, NULL, NULL); + + /* This test requires /usr/share/dict/words - a large list of English + * words. We load the entire file - odd lines are used as keys, and + * even lines are used as the values for the previous line. we then + * work through it again making sure everything matches. + * + * We do this twice - once in a hash table with many chains, and once + * with a hash table with fewer chains. + */ + + a = _dom_hash_create(1031, _dom_hash_pointer_fnv, test_alloc, NULL); + b = _dom_hash_create(7919, _dom_hash_pointer_fnv, test_alloc, NULL); + + dict = fopen("/usr/share/dict/words", "r"); + if (dict == NULL) { + fprintf(stderr, "Unable to open /usr/share/dict/words - \ + extensive testing skipped.\n"); + exit(0); + } + + while (!feof(dict)) { + fscanf(dict, "%s", keybuf); + fscanf(dict, "%s", valbuf); + _dom_hash_add(a, keybuf, valbuf, true); + _dom_hash_add(b, keybuf, valbuf, true); + } + + for (i = 0; i < 5; i++) { + fseek(dict, 0, SEEK_SET); + + while (!feof(dict)) { + fscanf(dict, "%s", keybuf); + fscanf(dict, "%s", valbuf); + assert(strcmp(hash_get(a, keybuf), valbuf) == 0); + assert(strcmp(hash_get(b, keybuf), valbuf) == 0); + } + } + + _dom_hash_destroy(a, NULL, NULL); + _dom_hash_destroy(b, NULL, NULL); + + fclose(dict); + + return 0; +} + +#endif diff --git a/src/utils/hashtable.h b/src/utils/hashtable.h new file mode 100644 index 0000000..3cfe95d --- /dev/null +++ b/src/utils/hashtable.h @@ -0,0 +1,42 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2006 Rob Kendrick + * Copyright 2009 Bo Yang + */ + +#ifndef dom_utils_hashtable_h_ +#define dom_utils_hashtable_h_ + +#include +#include + +typedef struct dom_hash_table dom_hash_table; +/* The hash function */ +typedef unsigned int (*dom_hash_func)(void *key); +/* Function to clone/delete key */ +typedef void *(*dom_key_func)(void *key, void *pw, dom_alloc alloc, + void *alloc_pw, bool clone); +/* Function to clone/delete value */ +typedef void *(*dom_value_func)(void *value, void *pw, dom_alloc alloc, + void *alloc_pw, bool clone); + +struct dom_hash_table *_dom_hash_create(unsigned int chains, dom_hash_func hash, + dom_alloc alloc, void *ptr); +struct dom_hash_table *_dom_hash_clone(struct dom_hash_table *ht, + dom_alloc alloc, void *pw, dom_key_func kf, void *key_pw, + dom_value_func vf, void *value_pw); +void _dom_hash_destroy(struct dom_hash_table *ht, dom_key_func kf, void *key_pw, + dom_value_func vf, void *value_pw); +bool _dom_hash_add(struct dom_hash_table *ht, void *key, void *value, + bool replace); +void *_dom_hash_get(struct dom_hash_table *ht, void *key); +void *_dom_hash_del(struct dom_hash_table *ht, void *key); +void *_dom_hash_iterate(struct dom_hash_table *ht, unsigned int *c1, + unsigned int **c2); +unsigned int _dom_hash_get_length(struct dom_hash_table *ht); +unsigned int _dom_hash_get_chains(struct dom_hash_table *ht); +dom_hash_func _dom_hash_get_func(struct dom_hash_table *ht); + +#endif diff --git a/src/utils/list.h b/src/utils/list.h new file mode 100644 index 0000000..6e3ba20 --- /dev/null +++ b/src/utils/list.h @@ -0,0 +1,61 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2009 Bo Yang + * + * This file contains the list structure used to compose lists. + * + * Note: This is a implementation of a doubld-linked cyclar list. + */ + +#ifndef dom_utils_list_h_ +#define dom_utils_list_h_ + +#include + +struct list_entry { + struct list_entry *prev; + struct list_entry *next; +}; + +/** + * Initialise a list_entry structure + * + * \param ent The entry to initialise + */ +static inline void list_init(struct list_entry *ent) +{ + ent->prev = ent; + ent->next = ent; +} + +/** + * Append a new list_entry after the list + * + * \param head The list header + * \param new The new entry + */ +static inline void list_append(struct list_entry *head, struct list_entry *new) +{ + new->next = head; + new->prev = head->prev; + head->prev->next = new; + head->prev = new; +} + +/** + * Delete a list_entry from the list + * + * \param entry The entry need to be deleted from the list + */ +static inline void list_del(struct list_entry *ent) +{ + ent->prev->next = ent->next; + ent->next->prev = ent->prev; + + ent->prev = ent; + ent->next = ent; +} + +#endif diff --git a/src/utils/namespace.c b/src/utils/namespace.c index ca5b01d..8d109ae 100644 --- a/src/utils/namespace.c +++ b/src/utils/namespace.c @@ -1,8 +1,9 @@ /* * This file is part of libdom. * Licensed under the MIT License, - * http://www.opensource.org/licenses/mit-license.php + * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell + * Copyright 2009 Bo Yang */ #include @@ -10,6 +11,7 @@ #include #include "utils/namespace.h" +#include "utils/validate.h" #include "utils/utils.h" @@ -18,7 +20,7 @@ static struct dom_string *xml; /** XMLNS prefix */ static struct dom_string *xmlns; -/** The namespace strings */ +/* The namespace strings */ static const char *namespaces[DOM_NAMESPACE_COUNT] = { NULL, "http://www.w3.org/1999/xhtml", @@ -37,7 +39,7 @@ struct dom_string *dom_namespaces[DOM_NAMESPACE_COUNT] = { * Initialise the namespace component * * \param alloc Pointer to memory (de)allocation function - * \param pw Pointer to client-specific private data + * \param pw Pointer to client-specific private data * \return DOM_NO_ERR on success. */ dom_exception _dom_namespace_initialise(dom_alloc alloc, void *pw) @@ -130,10 +132,19 @@ dom_exception _dom_namespace_finalise(void) dom_exception _dom_namespace_validate_qname(struct dom_string *qname, struct dom_string *namespace) { - uint32_t colon; + uint32_t colon, len; + + if (qname == NULL){ + if (namespace != NULL) + return DOM_NAMESPACE_ERR; + if (namespace == NULL) + return DOM_NO_ERR; + } + + if (_dom_validate_name(qname) == false) + return DOM_NAMESPACE_ERR; - /** \todo search qname for invalid characters */ - /** \todo ensure qname is not malformed */ + len = dom_string_length(qname); /* Find colon */ colon = dom_string_index(qname, ':'); @@ -147,9 +158,14 @@ dom_exception _dom_namespace_validate_qname(struct dom_string *qname, dom_string_cmp(qname, xmlns) != 0) { return DOM_NAMESPACE_ERR; } + } else if (colon == 0) { + /* Some name like ":name" */ + if (namespace != NULL) + return DOM_NAMESPACE_ERR; } else { /* Prefix */ struct dom_string *prefix; + struct dom_string *lname; dom_exception err; /* Ensure there is a namespace URI */ @@ -157,11 +173,21 @@ dom_exception _dom_namespace_validate_qname(struct dom_string *qname, return DOM_NAMESPACE_ERR; } - err = dom_string_substr(qname, 0, colon - 1, &prefix); + err = dom_string_substr(qname, 0, colon, &prefix); if (err != DOM_NO_ERR) { return err; } + err = dom_string_substr(qname, colon + 1, len, &lname); + if (err != DOM_NO_ERR) { + return err; + } + + if (_dom_validate_ncname(prefix) == false || + _dom_validate_ncname(lname) == false) { + return DOM_NAMESPACE_ERR; + } + /* Test for invalid XML namespace */ if (dom_string_cmp(prefix, xml) == 0 && dom_string_cmp(namespace, @@ -223,7 +249,7 @@ dom_exception _dom_namespace_split_qname(struct dom_string *qname, } } else { /* Found one => prefix */ - err = dom_string_substr(qname, 0, colon - 1, prefix); + err = dom_string_substr(qname, 0, colon, prefix); if (err != DOM_NO_ERR) { return err; } @@ -240,3 +266,32 @@ dom_exception _dom_namespace_split_qname(struct dom_string *qname, return DOM_NO_ERR; } +/** + * Get the XML prefix dom_string + * + * \return the xml prefix dom_string. + * + * Note: The client of this function may or may not call the dom_string_ref + * on the returned dom_string, because this string will only be destroyed when + * the dom_finalise is called. But if the client call dom_string_ref, it must + * call dom_string_unref to maintain a correct ref count of the dom_string. + */ +dom_string *_dom_namespace_get_xml_prefix(void) +{ + return xml; +} + +/** + * Get the XMLNS prefix dom_string. + * + * \return the xmlns prefix dom_string + * + * Note: The client of this function may or may not call the dom_string_ref + * on the returned dom_string, because this string will only be destroyed when + * the dom_finalise is called. But if the client call dom_string_ref, it must + * call dom_string_unref to maintain a correct ref count of the dom_string. + */ +dom_string *_dom_namespace_get_xmlns_prefix(void) +{ + return xmlns; +} diff --git a/src/utils/namespace.h b/src/utils/namespace.h index ec69035..900c9ee 100644 --- a/src/utils/namespace.h +++ b/src/utils/namespace.h @@ -14,6 +14,7 @@ struct dom_document; struct dom_string; + /* Initialise the namespace component */ dom_exception _dom_namespace_initialise(dom_alloc alloc, void *pw); @@ -28,5 +29,11 @@ dom_exception _dom_namespace_validate_qname(struct dom_string *qname, dom_exception _dom_namespace_split_qname(struct dom_string *qname, struct dom_string **prefix, struct dom_string **localname); +/* Get the XML prefix dom_string */ +struct dom_string *_dom_namespace_get_xml_prefix(void); + +/* Get the XMLNS prefix dom_string */ +struct dom_string *_dom_namespace_get_xmlns_prefix(void); + #endif diff --git a/src/utils/resource_mgr.c b/src/utils/resource_mgr.c new file mode 100644 index 0000000..c9c86d3 --- /dev/null +++ b/src/utils/resource_mgr.c @@ -0,0 +1,105 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2009 Bo Yang + */ + +#include "resource_mgr.h" + +#include +#include + +#include +#include "core/string.h" + +/** + * Allocate some memory with this allocator + * + * \param res The resource manager + * \param size The size of memory to allocate + * \return the allocated memory pointer. + */ +void *_dom_resource_mgr_alloc(struct dom_resource_mgr *res, void *ptr, + size_t size) +{ + return res->alloc(ptr, size, res->pw); +} + +/** + * Create a dom_string using this resource manager + * + * \param res The resource manager + * \param data The data pointer + * \param len The length of data + * \param result The returned dom_string + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_resource_mgr_create_string(struct dom_resource_mgr *res, + const uint8_t *data, size_t len, struct dom_string **result) +{ + return dom_string_create(res->alloc, res->pw, data, len, result); +} + +/** + * Create a lwc_string using this resource manager + * + * \param res The resource manager + * \param data The data pointer + * \param len The length of the data + * \param result The returned lwc_string + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_resource_mgr_create_lwcstring(struct dom_resource_mgr *res, + const uint8_t *data, size_t len, struct lwc_string_s **result) +{ + lwc_error lerr; + + assert(res->ctx != NULL); + + lerr = lwc_context_intern(res->ctx, (const char *) data, len, + result); + + return _dom_exception_from_lwc_error(lerr); +} + +/** + * Create a dom_string from a lwc_string using this resource manager + * + * \param res The resource manager + * \param str The dom_string to intern + * \param result The returned lwc_string + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_resource_mgr_create_string_from_lwcstring( + struct dom_resource_mgr *res, struct lwc_string_s *str, + struct dom_string **result) +{ + assert(res->ctx != NULL); + + return _dom_string_create_from_lwcstring(res->alloc, res->pw, res->ctx, + str, result); +} + +/** + * Create a hash table using this resource manager + * + * \param res The resource manager + * \param chains The number of buckets of the hash table + * \param f The hash function + * \param ht The returned hash table + * \return DOM_NO_ERR on success, appropriate dom_exception on failure. + */ +dom_exception _dom_resource_mgr_create_hashtable(struct dom_resource_mgr *res, + size_t chains, dom_hash_func f, struct dom_hash_table **ht) +{ + struct dom_hash_table *ret; + + ret = _dom_hash_create(chains, f, res->alloc, res->pw); + if (ret == NULL) + return DOM_NO_MEM_ERR; + + *ht = ret; + return DOM_NO_ERR; +} + diff --git a/src/utils/resource_mgr.h b/src/utils/resource_mgr.h new file mode 100644 index 0000000..b58f665 --- /dev/null +++ b/src/utils/resource_mgr.h @@ -0,0 +1,45 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2009 Bo Yang + */ + +#ifndef dom_utils_resource_mgr_h_ +#define dom_utils_resource_mgr_h_ + +#include +#include + +#include "hashtable.h" + +struct lwc_context_s; +struct lwc_string_s; +struct dom_string; + +/** + * Resource manager + */ +typedef struct dom_resource_mgr { + dom_alloc alloc; + void *pw; + struct lwc_context_s *ctx; +} dom_resource_mgr; + +void *_dom_resource_mgr_alloc(struct dom_resource_mgr *res, void *ptr, + size_t size); + +dom_exception _dom_resource_mgr_create_string(struct dom_resource_mgr *res, + const uint8_t *data, size_t len, struct dom_string **result); + +dom_exception _dom_resource_mgr_create_lwcstring(struct dom_resource_mgr *res, + const uint8_t *data, size_t len, struct lwc_string_s **result); + +dom_exception _dom_resource_mgr_create_string_from_lwcstring( + struct dom_resource_mgr *res, struct lwc_string_s *str, + struct dom_string **result); + +dom_exception _dom_resource_mgr_create_hashtable(struct dom_resource_mgr *res, + size_t chains, dom_hash_func f, struct dom_hash_table **ht); + +#endif diff --git a/src/utils/validate.c b/src/utils/validate.c new file mode 100644 index 0000000..eb6cb22 --- /dev/null +++ b/src/utils/validate.c @@ -0,0 +1,177 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2009 Bo Yang + */ + +#include +#include + +#include "utils/validate.h" + +#include + +#include "utils/character_valid.h" +#include "utils/namespace.h" +#include "utils/utils.h" + +/* An combination of various tests */ +static bool is_first_char(uint32_t ch); +static bool is_name_char(uint32_t ch); + +/* Test whether the character can be the first character of + * a NCName. */ +static bool is_first_char(uint32_t ch) +{ + /* Refer http://www.w3.org/TR/REC-xml/ for detail */ + if (((ch >= 'a') && (ch <= 'z')) || + ((ch >= 'A') && (ch <= 'Z')) || + (ch == '_') || (ch == ':') || + ((ch >= 0xC0) && (ch <= 0xD6)) || + ((ch >= 0xD8) && (ch <= 0xF6)) || + ((ch >= 0xF8) && (ch <= 0x2FF)) || + ((ch >= 0x370) && (ch <= 0x37D)) || + ((ch >= 0x37F) && (ch <= 0x1FFF)) || + ((ch >= 0x200C) && (ch <= 0x200D)) || + ((ch >= 0x2070) && (ch <= 0x218F)) || + ((ch >= 0x2C00) && (ch <= 0x2FEF)) || + ((ch >= 0x3001) && (ch <= 0xD7FF)) || + ((ch >= 0xF900) && (ch <= 0xFDCF)) || + ((ch >= 0xFDF0) && (ch <= 0xFFFD)) || + ((ch >= 0x10000) && (ch <= 0xEFFFF))) + return true; + + if (is_letter(ch) || ch == (uint32_t) '_' || ch == (uint32_t) ':') { + return true; + } + + return false; +} + +/* Test whether the character can be a part of a NCName */ +static bool is_name_char(uint32_t ch) +{ + /* Refer http://www.w3.org/TR/REC-xml/ for detail */ + if (((ch >= 'a') && (ch <= 'z')) || + ((ch >= 'A') && (ch <= 'Z')) || + ((ch >= '0') && (ch <= '9')) || /* !start */ + (ch == '_') || (ch == ':') || + (ch == '-') || (ch == '.') || (ch == 0xB7) || /* !start */ + ((ch >= 0xC0) && (ch <= 0xD6)) || + ((ch >= 0xD8) && (ch <= 0xF6)) || + ((ch >= 0xF8) && (ch <= 0x2FF)) || + ((ch >= 0x300) && (ch <= 0x36F)) || /* !start */ + ((ch >= 0x370) && (ch <= 0x37D)) || + ((ch >= 0x37F) && (ch <= 0x1FFF)) || + ((ch >= 0x200C) && (ch <= 0x200D)) || + ((ch >= 0x203F) && (ch <= 0x2040)) || /* !start */ + ((ch >= 0x2070) && (ch <= 0x218F)) || + ((ch >= 0x2C00) && (ch <= 0x2FEF)) || + ((ch >= 0x3001) && (ch <= 0xD7FF)) || + ((ch >= 0xF900) && (ch <= 0xFDCF)) || + ((ch >= 0xFDF0) && (ch <= 0xFFFD)) || + ((ch >= 0x10000) && (ch <= 0xEFFFF))) + return true; + + if (is_letter(ch) == true) + return true; + if (is_digit(ch) == true) + return true; + if (is_combining_char(ch) == true) + return true; + if (is_extender(ch) == true) + return true; + + if (ch == (uint32_t) '.' || ch == (uint32_t) '-' || + ch == (uint32_t) '_' || ch == (uint32_t) ':') + return true; + + return false; +} + +/** + * Test whether the name is a valid one according XML 1.0 standard. + * For the standard please refer: + * + * http://www.w3.org/TR/2004/REC-xml-20040204/ + * + * \param name The name need to be tested + * \return true if ::name is valid, false otherwise. + */ +bool _dom_validate_name(struct dom_string *name) +{ + uint32_t ch, len, i; + dom_exception err; + + if (name == NULL) + return false; + + len = dom_string_length(name); + if (len == 0) + return false; + + /* Test the first character of this string */ + err = dom_string_at(name, 0, &ch); + if (err != DOM_NO_ERR) + return false; + + if (is_first_char(ch) == false) + return false; + + /* Test all remain characters in this string */ + for(i = 1; i < len; i++) { + err = dom_string_at(name, i, &ch); + if (err != DOM_NO_ERR) + return false; + + if (is_name_char(ch) != true) + return false; + } + + return true; +} + +/** + * Validate whether the string is a legal NCName. + * Refer http://www.w3.org/TR/REC-xml-names/ for detail. + * + * \param str The name to validate + * \return true if ::name is valid, false otherwise. + */ +bool _dom_validate_ncname(struct dom_string *name) +{ + uint32_t ch, len, i; + dom_exception err; + + if (name == NULL) + return false; + + len = dom_string_length(name); + if (len == 0) + return false; + + /* Test the first character of this string */ + err = dom_string_at(name, 0, &ch); + if (err != DOM_NO_ERR) + return false; + + if (is_letter(ch) == false && ch != (uint32_t) '_') + return false; + + /* Test all remain characters in this string */ + for(i = 1; i < len; i++) { + err = dom_string_at(name, i, &ch); + if (err != DOM_NO_ERR) + return false; + + if (is_name_char(ch) == false) + return false; + + if (ch == (uint32_t) ':') + return false; + } + + return true; +} + diff --git a/src/utils/validate.h b/src/utils/validate.h new file mode 100644 index 0000000..5d375e7 --- /dev/null +++ b/src/utils/validate.h @@ -0,0 +1,26 @@ +/* + * This file is part of libdom. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2009 Bo Yang + * + * This file contains the API used to validate whether certain element's + * name/namespace are legal according the XML 1.0 standard. See + * + * http://www.w3.org/TR/2004/REC-xml-20040204/ + * + * for detail. + */ + +#ifndef dom_utils_valid_h_ +#define dom_utils_valid_h_ + +#include + +struct dom_string; + +bool _dom_validate_name(struct dom_string *name); +bool _dom_validate_ncname(struct dom_string *name); + +#endif + -- cgit v1.2.3