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