/* * 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 */ #include #include #include #include #include #include #include #include #include "core/attr.h" #include "core/document.h" #include "core/entity_ref.h" #include "core/node.h" #include "core/element.h" #include "utils/utils.h" struct dom_element; /** * DOM attribute node */ struct dom_attr { struct dom_node_internal base; /**< Base node */ struct dom_type_info *schema_type_info; /**< Type information */ dom_attr_type type; /**< The type of this attribute */ union { uint32_t lvalue; unsigned short svalue; bool bvalue; } value; /**< The special type value of this attribute */ bool specified; /**< Whether the attribute is specified or default */ bool is_id; /**< Whether this attribute is a ID attribute */ bool read_only; /**< Whether this attribute is readonly */ }; /* The vtable for dom_attr node */ static struct dom_attr_vtable attr_vtable = { { { DOM_NODE_EVENT_TARGET_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 * * \param doc The owning document * \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 Whether this attribute is specified * \param result Pointer to location to receive created attribute * \return DOM_NO_ERR on success, * DOM_NO_MEM_ERR on memory exhaustion. * * \p doc and \p name will have their reference counts increased. The * caller should make sure that \p name is a valid NCName here. * * The returned attribute will already be referenced. */ dom_exception _dom_attr_create(struct dom_document *doc, dom_string *name, dom_string *namespace, dom_string *prefix, bool specified, struct dom_attr **result) { struct dom_attr *a; dom_exception err; /* Allocate the attribute node */ a = malloc(sizeof(struct dom_attr)); if (a == NULL) return DOM_NO_MEM_ERR; /* Initialise the vtable */ a->base.base.vtable = &attr_vtable; 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) { free(a); 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, dom_string *name, dom_string *namespace, dom_string *prefix, bool specified, struct dom_attr **result) { dom_exception err; err = _dom_node_initialise(&a->base, doc, DOM_ATTRIBUTE_NODE, name, NULL, namespace, prefix); if (err != DOM_NO_ERR) { return err; } a->specified = specified; a->schema_type_info = NULL; a->is_id = false; /* The attribute type is unset when it is created */ a->type = DOM_ATTR_UNSET; a->read_only = false; *result = a; return DOM_NO_ERR; } /** * The destructor of dom_attr * * \param attr The attribute */ void _dom_attr_finalise(dom_attr *attr) { /* Now, clean up this node and destroy it */ if (attr->schema_type_info != NULL) { /** \todo destroy schema type info */ } _dom_node_finalise(&attr->base); } /** * Destroy an attribute node * * \param attr The attribute to destroy * * The contents of \p attr will be destroyed and \p attr will be freed */ void _dom_attr_destroy(struct dom_attr *attr) { _dom_attr_finalise(attr); free(attr); } /*-----------------------------------------------------------------------*/ /* Following are our implementation specific APIs */ /** * Get the Attr Node type * * \param a The attribute node * \return the type */ dom_attr_type dom_attr_get_type(dom_attr *a) { return a->type; } /** * Get the integer value of this attribute * * \param a The attribute object * \param value The returned value * \return DOM_NO_ERR on success, * DOM_ATTR_WRONG_TYPE_ERR if the attribute node is not a integer * attribute */ dom_exception dom_attr_get_integer(dom_attr *a, uint32_t *value) { if (a->type != DOM_ATTR_INTEGER) return DOM_ATTR_WRONG_TYPE_ERR; *value = a->value.lvalue; return DOM_NO_ERR; } /** * Set the integer value of this attribute * * \param a The attribute object * \param value The new value * \return DOM_NO_ERR on success, * DOM_ATTR_WRONG_TYPE_ERR if the attribute node is not a integer * attribute */ dom_exception dom_attr_set_integer(dom_attr *a, uint32_t value) { struct dom_document *doc; struct dom_node_internal *ele; bool success = true; dom_exception err; /* If this is the first set method, we should fix this attribute * type */ if (a->type == DOM_ATTR_UNSET) a->type = DOM_ATTR_INTEGER; if (a->type != DOM_ATTR_INTEGER) return DOM_ATTR_WRONG_TYPE_ERR; if (a->value.lvalue == value) return DOM_NO_ERR; a->value.lvalue = value; doc = dom_node_get_owner(a); ele = dom_node_get_parent(a); err = _dom_dispatch_attr_modified_event(doc, ele, NULL, NULL, (dom_event_target *) a, NULL, DOM_MUTATION_MODIFICATION, &success); if (err != DOM_NO_ERR) return err; success = true; err = _dom_dispatch_subtree_modified_event(doc, (dom_event_target *) a, &success); return err; } /** * Get the short value of this attribute * * \param a The attribute object * \param value The returned value * \return DOM_NO_ERR on success, * DOM_ATTR_WRONG_TYPE_ERR if the attribute node is not a short * attribute */ dom_exception dom_attr_get_short(dom_attr *a, unsigned short *value) { if (a->type != DOM_ATTR_SHORT) return DOM_ATTR_WRONG_TYPE_ERR; *value = a->value.svalue; return DOM_NO_ERR; } /** * Set the short value of this attribute * * \param a The attribute object * \param value The new value * \return DOM_NO_ERR on success, * DOM_ATTR_WRONG_TYPE_ERR if the attribute node is not a short * attribute */ dom_exception dom_attr_set_short(dom_attr *a, unsigned short value) { struct dom_document *doc; struct dom_node_internal *ele; bool success = true; dom_exception err; /* If this is the first set method, we should fix this attribute * type */ if (a->type == DOM_ATTR_UNSET) a->type = DOM_ATTR_SHORT; if (a->type != DOM_ATTR_SHORT) return DOM_ATTR_WRONG_TYPE_ERR; if (a->value.svalue == value) return DOM_NO_ERR; a->value.svalue = value; doc = dom_node_get_owner(a); ele = dom_node_get_parent(a); err = _dom_dispatch_attr_modified_event(doc, ele, NULL, NULL, (dom_event_target *) a, NULL, DOM_MUTATION_MODIFICATION, &success); if (err != DOM_NO_ERR) return err; success = true; err = _dom_dispatch_subtree_modified_event(doc, (dom_event_target *) a, &success); return err; } /** * Get the bool value of this attribute * * \param a The attribute object * \param value The returned value * \return DOM_NO_ERR on success, * DOM_ATTR_WRONG_TYPE_ERR if the attribute node is not a bool * attribute */ dom_exception dom_attr_get_bool(dom_attr *a, bool *value) { if (a->type != DOM_ATTR_BOOL) return DOM_ATTR_WRONG_TYPE_ERR; *value = a->value.bvalue; return DOM_NO_ERR; } /** * Set the bool value of this attribute * * \param a The attribute object * \param value The new value * \return DOM_NO_ERR on success, * DOM_ATTR_WRONG_TYPE_ERR if the attribute node is not a bool * attribute */ dom_exception dom_attr_set_bool(dom_attr *a, bool value) { struct dom_document *doc; struct dom_node_internal *ele; bool success = true; dom_exception err; /* If this is the first set method, we should fix this attribute * type */ if (a->type == DOM_ATTR_UNSET) a->type = DOM_ATTR_BOOL; if (a->type != DOM_ATTR_BOOL) return DOM_ATTR_WRONG_TYPE_ERR; if (a->value.bvalue == value) return DOM_NO_ERR; a->value.bvalue = value; doc = dom_node_get_owner(a); ele = dom_node_get_parent(a); err = _dom_dispatch_attr_modified_event(doc, ele, NULL, NULL, (dom_event_target *) a, NULL, DOM_MUTATION_MODIFICATION, &success); if (err != DOM_NO_ERR) return err; success = true; err = _dom_dispatch_subtree_modified_event(doc, (dom_event_target *) a, &success); return err; } /** * Set the node as a readonly attribute * * \param a The attribute */ void dom_attr_mark_readonly(dom_attr *a) { a->read_only = true; } /* -------------------------------------------------------------------- */ /* 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 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 * finished with it. */ dom_exception _dom_attr_get_name(struct dom_attr *attr, dom_string **result) { /* This is the same as nodeName */ return dom_node_get_node_name(attr, result); } /** * Determine if attribute was specified or default * * \param attr Attribute to inspect * \param result Pointer to location to receive result * \return DOM_NO_ERR. */ dom_exception _dom_attr_get_specified(struct dom_attr *attr, bool *result) { *result = attr->specified; return DOM_NO_ERR; } /** * Retrieve an attribute's value * * \param attr Attribute to retrieve value from * \param result Pointer to location to receive result * \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 * finished with it. */ dom_exception _dom_attr_get_value(struct dom_attr *attr, dom_string **result) { struct dom_node_internal *a = (struct dom_node_internal *) attr; struct dom_node_internal *c; dom_string *value, *temp; dom_exception err; /* Attempt to shortcut for a single text node child with value */ if ((a->first_child != NULL) && (a->first_child == a->last_child) && (a->first_child->type == DOM_TEXT_NODE) && (a->first_child->value != NULL)) { *result = dom_string_ref(a->first_child->value); return DOM_NO_ERR; } err = dom_string_create(NULL, 0, &value); if (err != DOM_NO_ERR) { return err; } /* Force unknown types to strings, if necessary */ if (attr->type == DOM_ATTR_UNSET && a->first_child != NULL) { attr->type = DOM_ATTR_STRING; } /* If this attribute node is not a string one, we just return an empty * string */ if (attr->type != DOM_ATTR_STRING) { *result = value; return DOM_NO_ERR; } /* Traverse children, building a string representation as we go */ for (c = a->first_child; c != NULL; c = c->next) { if (c->type == DOM_TEXT_NODE && c->value != NULL) { /* Append to existing value */ err = dom_string_concat(value, c->value, &temp); if (err != DOM_NO_ERR) { dom_string_unref(value); return err; } /* Finished with previous value */ dom_string_unref(value); /* Claim new value */ value = temp; } else if (c->type == DOM_ENTITY_REFERENCE_NODE) { dom_string *tr; /* Get textual representation of entity */ err = _dom_entity_reference_get_textual_representation( (struct dom_entity_reference *) c, &tr); if (err != DOM_NO_ERR) { dom_string_unref(value); return err; } /* Append to existing value */ err = dom_string_concat(value, tr, &temp); if (err != DOM_NO_ERR) { dom_string_unref(tr); dom_string_unref(value); return err; } /* No int32_ter need textual representation */ dom_string_unref(tr); /* Finished with previous value */ dom_string_unref(value); /* Claim new value */ value = temp; } } *result = value; return DOM_NO_ERR; } /** * Set an attribute's value * * \param attr Attribute to retrieve value from * \param value New value for attribute * \return DOM_NO_ERR on success, * DOM_NO_MODIFICATION_ALLOWED_ERR if attribute is readonly. */ dom_exception _dom_attr_set_value(struct dom_attr *attr, dom_string *value) { struct dom_node_internal *a = (struct dom_node_internal *) attr; struct dom_node_internal *c, *d; struct dom_text *text; dom_exception err; dom_string *name = NULL; dom_string *parsed = NULL; /* Ensure attribute is writable */ if (_dom_node_readonly(a)) return DOM_NO_MODIFICATION_ALLOWED_ERR; /* If this is the first set method, we should fix this attribute * type */ if (attr->type == DOM_ATTR_UNSET) attr->type = DOM_ATTR_STRING; if (attr->type != DOM_ATTR_STRING) return DOM_ATTR_WRONG_TYPE_ERR; err = _dom_attr_get_name(attr, &name); if (err != DOM_NO_ERR) return err; err = dom_element_parse_attribute(a->parent, name, value, &parsed); dom_string_unref(name); if (err != DOM_NO_ERR) { return err; } /* Create text node containing new value */ err = dom_document_create_text_node(a->owner, parsed, &text); dom_string_unref(parsed); if (err != DOM_NO_ERR) return err; /* Destroy children of this node */ for (c = a->first_child; c != NULL; c = d) { d = c->next; /* Detach child */ c->parent = NULL; /* Detach from sibling list */ c->previous = NULL; c->next = NULL; 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; } /** * Retrieve the owning element of an attribute * * \param attr The attribute to extract owning element from * \param result Pointer to location to receive result * \return DOM_NO_ERR. * * The returned node will have its reference count increased. The caller * should unref it once it has finished with it. */ dom_exception _dom_attr_get_owner(struct dom_attr *attr, struct dom_element **result) { struct dom_node_internal *a = (struct dom_node_internal *) attr; /* If there is an owning element, increase its reference count */ if (a->parent != NULL) dom_node_ref(a->parent); *result = (struct dom_element *) a->parent; return DOM_NO_ERR; } /** * Retrieve an attribute's type information * * \param attr The attribute to extract type information from * \param result Pointer to location to receive result * \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. */ dom_exception _dom_attr_get_schema_type_info(struct dom_attr *attr, struct dom_type_info **result) { UNUSED(attr); UNUSED(result); return DOM_NOT_SUPPORTED_ERR; } /** * Determine if an attribute if of type ID * * \param attr The attribute to inspect * \param result Pointer to location to receive result * \return DOM_NO_ERR. */ dom_exception _dom_attr_is_id(struct dom_attr *attr, bool *result) { *result = attr->is_id; 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, 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, 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, dom_string *namespace, 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, 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, dom_string *prefix, 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_attr_destroy((dom_attr *) node); } /* The memory allocator of this class */ dom_exception _dom_attr_copy(dom_node_internal *n, dom_node_internal **copy) { dom_attr *old = (dom_attr *) n; dom_attr *a; dom_exception err; a = malloc(sizeof(struct dom_attr)); if (a == NULL) return DOM_NO_MEM_ERR; err = dom_node_copy_internal(n, a); if (err != DOM_NO_ERR) { free(a); return err; } a->specified = old->specified; /* TODO: deal with dom_type_info, it get no definition ! */ a->schema_type_info = NULL; a->is_id = old->is_id; a->type = old->type; a->value = old->value; /* TODO: is this correct? */ a->read_only = false; *copy = (dom_node_internal *) a; return DOM_NO_ERR; } /** * 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; } /** * Whether this attribute node is readonly * * \param a The node * \return true if this Attr is readonly, false otherwise */ bool _dom_attr_readonly(const dom_attr *a) { return a->read_only; }