Hi,

On Fri, 2006-02-24 at 14:57 +0100, Kasimier Buchcik wrote:

[...]

> My intention is now just try to implement some prototypes explicitely, 
> i.e., without trying to reuse the code base, then send it to the
> list for discussion, then maybe commit and then we still can try to
> eliminate redundant code and make the code base more flexible.
> Is this OK with you all?

I attached the current prototypes for setting/adding DOM attrs.

Compared to the default code base, I mostly see the following
differences (this is written from the DOM-viewpoint):

1) Adding an attr-node
  a) we need a removed attr-node to be returned
  b) adjustable IDness handling (in xmlDOMWrapRegisterIfId())

2) Setting attrs
  a) DOM: "value is a simple string; it is not parsed as it is being
           set"
  b) same as 1b)

Some other internal functions come with this, but they might not be
needed; I just wanted to minimize the relevant code.

Regards,

Kasimier

#define IS_STR_XML(str) ((str != NULL) && (str[0] == 'x') && \
  (str[1] == 'm') && (str[2] == 'l') && (str[3] == 0))

#define IS_STR_ID(str) ((str != NULL) && (str[0] == 'i') && \
  (str[1] == 'd') && (str[2] == 0))

#define DOC_DICT_STRDUP(doc, dest, str) \
    if ((doc != NULL) && (doc->dict != NULL)) \
	dest = (xmlChar *) xmlDictLookup(doc->dict, str, -1); \
    else \
	dest = xmlStrdup(str);

#define HAS_DOC_DTD(doc) ((doc != NULL) && \
    ((doc->intSubset != NULL) || (doc->extSubset != NULL)))

/*
* xmlGetNoNsPropNode:
*
* NOTE that we don't have a mechanism to distinguish between
* DOM level 1 and level 2 attrs.
*/
static xmlAttrPtr
xmlGetNoNsPropNode(xmlNodePtr node, const xmlChar *name)
{

    if ((node == NULL) || (name == NULL) ||
	(node->properties == NULL))
    {
	return(NULL);
    } else {
	xmlAttrPtr cur = node->properties;
	do {
	    if ((cur->ns = NULL) &&
		((cur->name == name) || xmlStrEqual(cur->name, name)))
	    {
		return(cur);
	    }
	    cur = cur->next;
	} while (cur != NULL);
    }
    return(NULL);
}

static xmlAttrPtr
xmlGetNsPropNode(xmlNodePtr node, const xmlChar *name,
		 const xmlChar *namespaceName)
{

    if ((node == NULL) || (name == NULL) ||
	(node->properties == NULL))
    {
	return(NULL);
    } else {
	xmlAttrPtr cur = node->properties;
	do {
	    if ((cur->ns) &&
		((cur->name == name) || xmlStrEqual(cur->name, name)) &&
		((cur->ns->href == namespaceName) ||
		 xmlStrEqual(cur->ns->href, namespaceName)))
	    {
		return(cur);
	    }
	    cur = cur->next;
	} while (cur != NULL);
    }
    return(NULL);
}

static xmlAttrPtr
xmlTreeNewPropRaw(xmlDocPtr doc, const xmlChar *name, xmlNsPtr ns)
{
    xmlAttrPtr res;

    res = (xmlAttrPtr) xmlMalloc(sizeof(xmlAttr));
    if (res == NULL) {
        xmlTreeErrMemory("building attribute");
        return (NULL);
    }
    memset(res, 0, sizeof(xmlAttr));
    res->type = XML_ATTRIBUTE_NODE;
    DOC_DICT_STRDUP(doc, res->name, name);
    res->ns = ns;
    return(res);
}

static int
xmlDOMWrapRegisterIfId(xmlAttrPtr attr,
		      int isId, int isIdSchemaAware)
{
    xmlNodePtr cur;

    if (attr == NULL)
	return(-1);

    if ((attr->doc == NULL) || (attr->parent == NULL) ||
	(attr->children == NULL))
	return(0);    
    /*
    * We want the attr to be attached in the doc's tree.
    */
    cur = attr->parent;
    while (cur->parent != NULL)
	cur = cur->parent;

    if ((cur->type != XML_DOCUMENT_NODE) &&
#ifdef LIBXML_DOCB_ENABLED
	(cur->type != XML_DOCB_DOCUMENT_NODE) &&
#endif
	(cur->type != XML_HTML_DOCUMENT_NODE))
	return(0);

    /* Paranoid */
    if (attr->doc != (xmlDocPtr) cur)
	return(-1);
    
    /*
    * We want to register IDs if...
    */
    if (attr->atype == XML_ATTRIBUTE_ID) {
	/*
	* Preserved IDness.
	*/
	goto registerId;
    } else if (isId) {
	/*
	* "user-determined ID attribute": Register the ID value.
	* xmlAddID() sets: attr->atype = XML_ATTRIBUTE_ID;
	*/
	goto registerId;
    } else if ((attr->ns != NULL) && IS_STR_ID(attr->name) &&
	IS_STR_XML(attr->ns->prefix))
    {
	/*
	* xml:id.
	* TODO: Not sure about this one: does it really automatically
	*   create an ID entry? Or do we still have to use
	*   Element.setIdAttributeNode() even for xml:id with DOM?
	*/
	goto registerId;
    } else if (isIdSchemaAware) {
	/*	    
	* "DTD-determined ID attribute".
	*/
	if (HAS_DOC_DTD(attr->doc) &&
	    (xmlIsID(attr->doc, attr->parent, attr) == 1))
	    goto registerId;
	/*
	* REVISIT: "schema-determined ID attribute".
	*/
    }
    return(0);

registerId:
    {
	const xmlChar *value;	

	if ((attr->children == attr->last) &&
	    ((attr->children->type == XML_TEXT_NODE) ||
	    (attr->children->type == XML_CDATA_SECTION_NODE)))
	{
	    /*
	    * The most common case: only 1 text-node; no need
	    * for duplicating the content.
	    */
	    value = attr->children->content;
	    if (value == NULL)
		return(0);
	    if (xmlAddID(NULL, attr->doc, value, attr) == NULL)
		return(-1);
	} else {
	    xmlIDPtr id;
	    value = xmlNodeListGetString(attr->doc, attr->children, 1);
	    if (value != NULL) {
		id = xmlAddID(NULL, attr->doc, value, attr);
		xmlFree(id);
		if (id == NULL)
		    return(-1);
	    }
	}
    }
    return(0);
}

/*
* xmlDOMWrapSetAttrNode:
* 
* @ctxt: The DOM wrapper context (optional)
* @doc: The document-node
* @ns: The namespace of the attribute
* @name: The name of the attribute
* @value: The value of the attribute (optional)
* @resAttr: The created attribute, if any
* @options: Options
*
* Sets an attribute and its value DOM-wise.
*
* Returns 0 if succeeded, -1 on internal or API errors.
*/
static int
xmlDOMWrapSetAttrNode(xmlDOMWrapCtxtPtr ctxt,
		  xmlDocPtr doc, xmlNodePtr owner,
		  xmlAttrPtr attr, xmlAttrPtr *removedAttr,
		  int options,
		  /* Those are just temporary explicit options
		     to ease the design */
		  int registerId,
		  int isId,
		  int isIdSchemaAware)
{
    if ((doc == NULL) || (owner == NULL) || (removedAttr == NULL))
	return(-1);

    /*
    * TODO: Shall we expect the attr to be unliked already?
    *       What about the doc, need the correct doc to be set
    *       on @attr already?
    */
    if (attr->parent != NULL)
	return(-1);

    *removedAttr = NULL;

    if (owner->properties != NULL) {
	xmlAttrPtr oldattr;
	/*
	* Eval if there already exists such an attr.
	*/
	if (attr->ns == NULL)
	    oldattr = xmlGetNoNsPropNode(owner, attr->name);
	else {
	    oldattr = xmlGetNsPropNode(owner, attr->name, attr->ns->href);		    
	    if (oldattr != NULL) {
		/*
		* We need to remove the old attribute. Play save and return
		* this attr even if the removal failed.
		*/
		*removedAttr = oldattr;
		if (xmlDOMWrapRemoveNode(ctxt, doc, (xmlNodePtr) oldattr, 0) == -1)
		    goto internal_error;	
	    }
	}	
    }	
    if (registerId && (attr->children != NULL)) {
	/*
	* Register IDs if wanted.
	*/
	if (registerId) {
	    if (xmlDOMWrapRegisterIfId(attr, isId,
		isIdSchemaAware) == -1)
		goto internal_error;
	}
    }
    /*
    * Attach the attribute to the parent elem.
    */
    attr->parent = owner;
    if (owner->properties == NULL) {
	owner->properties = attr;
    } else {
	xmlAttrPtr prev = owner->properties;
	
	while (prev->next != NULL)
	    prev = prev->next;
	prev->next = attr;
	attr->prev = prev;
    } 
    return(0);

internal_error:
    return(-1);
}

/*
* xmlDOMWrapSetAttr:
* 
* @ctxt: The DOM wrapper context (optional)
* @doc: The document-node
* @ns: The namespace of the attribute
* @name: The name of the attribute
* @value: The value of the attribute (optional)
* @resAttr: The created attribute, if any
* @options: Options
*
* Sets an attribute and its value DOM-wise.
*
* Returns 0 if succeeded, -1 on internal or API errors.
*/
static int
xmlDOMWrapSetAttr(xmlDOMWrapCtxtPtr ctxt,
		  xmlDocPtr doc, xmlNodePtr owner,
		  xmlNsPtr ns,
		  const xmlChar *name, const xmlChar *value,
		  xmlAttrPtr *resAttr,
		  int options,
		  /* Those are just temporary explicit options
		     to ease the design */
		  int registerId,		  
		  int isId,
		  int preserveId,		  
		  int isIdSchemaAware)
{
    xmlAttrPtr attr = NULL;
    int ret = 0, created = 0;

    if ((doc == NULL) || (owner == NULL) || (resAttr == NULL))
	return(-1);

    *resAttr = NULL;

    if (owner->properties != NULL) {
	/*
	* Eval if there already exists such an attr.
	*/
	if (ns == NULL)
	    attr = xmlGetNoNsPropNode(owner, name);
	else {
	    attr = xmlGetNsPropNode(owner, name, ns->href);
	    /*
	    * Changes the prefix if different.
	    */
	    attr->ns = ns;
	}	
    }	
    if (attr == NULL) {
	/*
	* Create a new attribute-node.
	*/
	attr = xmlTreeNewPropRaw(doc, name, ns);	
	if (attr == NULL)
	    goto internal_error;
	attr->parent = owner;
	created = 1;
    } else if (attr->children != NULL) {
	/*
	* Free the old content.
	*/
	if ((doc != NULL) && (attr->atype == XML_ATTRIBUTE_ID)) {
	    if (xmlRemoveID(doc, attr) == -1)
		goto internal_error;
	    /*
	    * Preserve IDness if we have a value.
	    */
	    if (value != NULL)
		attr->atype = XML_ATTRIBUTE_ID;
	}
	xmlFreeNodeList(attr->children);
	attr->children = NULL;
	attr->last = NULL;
    }
    /*
    * Assign new value.
    */	
    if (value != NULL) {
	/*
	* DOM:
	* "This value is a simple string; it is not parsed as it is
	* being set. So any markup (such as syntax to be recognized
	* as an entity reference) is treated as literal text, and
	* needs to be appropriately escaped by the implementation
	* when it is written out"
	*
	* TODO: Use dict for the value?
	*/
	attr->children = xmlNewDocText(doc, value);
	if (attr->children == NULL)
	    goto internal_error;
	/*
	* Register IDs if wanted.
	*/
	if (registerId) {
	    if (xmlDOMWrapRegisterIfId(attr, isId,
		isIdSchemaAware) == -1)
		goto internal_error;
	}
    }
    /*
    * Attach the attribute to the parent elem.
    */
    if (owner->properties == NULL) {
	owner->properties = attr;
    } else {
	xmlAttrPtr prev = owner->properties;
	
	while (prev->next != NULL)
	    prev = prev->next;
	prev->next = attr;
	attr->prev = prev;
    } 

    if ((created) && (__xmlRegisterCallbacks) &&
	(xmlRegisterNodeDefaultValue))
        xmlRegisterNodeDefaultValue((xmlNodePtr) attr);

    *resAttr = attr;
    goto exit;

internal_error:
    ret = -1;

exit:
    /*
    * TODO: Think about cleanup on errors.
    */
    return(ret);
}
_______________________________________________
xml mailing list, project page  http://xmlsoft.org/
[email protected]
http://mail.gnome.org/mailman/listinfo/xml

Reply via email to