/* $Id: dom.c,v 1.28 2002/03/13 13:57:08 phish Exp $ */
#include <libxml/tree.h>
#include <libxml/encoding.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/xmlIO.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <stdio.h>
#ifdef XS_WARNINGS
#define xs_warn(string) fprintf(stderr, string)
#else
#define xs_warn(string)
#endif
xmlDocPtr
domCreateDocument( xmlChar *version, xmlChar *enc ){
xmlDocPtr doc = NULL;
doc = xmlNewDoc( version );
doc->charset = XML_CHAR_ENCODING_UTF8;
if ( enc != NULL && *enc!= 0 ) {
/* if an encoding is passed, we will assume UTF8, otherwise we set
* the passed encoding
*/
doc->encoding = xmlStrdup(enc);
}
else {
doc->encoding = xmlStrdup("UTF-8");
}
return doc;
}
/**
* Name: domReadWellBalancedString
* Synopsis: xmlNodePtr domReadWellBalancedString( xmlDocPtr doc, xmlChar *string )
* @doc: the document, the string should belong to
* @string: the string to parse
*
* this function is pretty neat, since you can read in well balanced
* strings and get a list of nodes, which can be added to any other node.
* (shure - this should return a doucment_fragment, but still it doesn't)
*
* the code is pretty heavy i think, but deep in my heard i believe it's
* worth it :) (e.g. if you like to read a chunk of well-balanced code
* from a databasefield)
*
* in 99% the cases i believe it is faster than to create the dom by hand,
* and skip the parsing job which has to be done here.
**/
void
_fix_node( xmlNodePtr node, xmlDocPtr doc ) {
/* internal helper for domReadWellBalancedString */
/* libxml2 creates an internal document for the chunk. this is ok
for flat chunks. chunks with nested chunks will keep a
reference to that internal document. this will lead into
segfaults, if the child nodes are not fixed. */
if ( node != NULL
&& node->type != XML_DTD_NODE
&& node->type != XML_NAMESPACE_DECL
&& node->doc != NULL ) {
node->doc = doc;
if ( node->children != NULL )
_fix_node( node->children, doc );
_fix_node( node->next , doc );
}
}
xmlNodePtr
domReadWellBalancedString( xmlDocPtr doc, xmlChar* block ) {
int retCode = -1;
xmlNodePtr helper = NULL;
xmlNodePtr nodes = NULL;
if ( block ) {
/* read and encode the chunk */
retCode = xmlParseBalancedChunkMemory( doc,
NULL,
NULL,
0,
block,
&nodes );
/* error handling */
if ( retCode != 0 ) {
/* if the code was not well balanced, we will not return
* a bad node list, but we have to free the nodes */
while( nodes != NULL ) {
helper = nodes->next;
xmlFreeNode( nodes );
nodes = helper;
}
}
else {
_fix_node( nodes, doc );
}
}
return nodes;
}
/**
* encodeString returns an UTF-8 encoded String
* while the encodig has the name of the encoding of string
**/
xmlChar*
domEncodeString( const char *encoding, const char *string ){
xmlCharEncoding enc;
xmlChar *ret = NULL;
xmlBufferPtr in, out;
xmlCharEncodingHandlerPtr coder = NULL;
if ( string != NULL ) {
if( encoding != NULL ) {
enc = xmlParseCharEncoding( encoding );
if ( enc > 1 ) {
coder= xmlGetCharEncodingHandler( enc );
}
else if ( enc == 1 ) {
ret = xmlStrdup( string );
}
else if ( enc == XML_CHAR_ENCODING_ERROR ){
xs_warn("no standard encoding\n");
coder = xmlFindCharEncodingHandler( encoding );
}
else {
xs_warn("no encoding found\n");
}
if ( coder != NULL ) {
xs_warn("coding machine found\n");
in = xmlBufferCreate();
out = xmlBufferCreate();
xmlBufferCCat( in, string );
if ( xmlCharEncInFunc( coder, out, in ) >= 0 ) {
ret = xmlStrdup( out->content );
}
else {
xs_warn( "b0rked encoiding!\n");
}
xmlBufferFree( in );
xmlBufferFree( out );
}
else {
xs_warn("no coder found\n");
}
}
else {
/* if utf-8 is requested we do nothing */
ret = xmlStrdup( string );
}
}
return ret;
}
/**
* decodeString returns an $encoding encoded string.
* while string is an UTF-8 encoded string and
* encoding is the coding name
**/
char*
domDecodeString( const char *encoding, const xmlChar *string){
char *ret=NULL;
xmlCharEncoding enc;
xmlBufferPtr in, out;
xmlCharEncodingHandlerPtr coder = NULL;
if ( string != NULL ) {
if( encoding != NULL ) {
enc = xmlParseCharEncoding( encoding );
if ( enc > 1 ) {
coder= xmlGetCharEncodingHandler( enc );
}
else if ( enc == 1 ) {
ret = xmlStrdup( string );
}
else if ( enc == XML_CHAR_ENCODING_ERROR ) {
coder = xmlFindCharEncodingHandler( encoding );
}
else {
xs_warn("no encoding found");
}
if ( coder != NULL ) {
in = xmlBufferCreate();
out = xmlBufferCreate();
xmlBufferCat( in, string );
if ( xmlCharEncOutFunc( coder, out, in ) >= 0 ) {
ret=xmlStrdup(out->content);
}
else {
/* printf("decoding error \n"); */
}
xmlBufferFree( in );
xmlBufferFree( out );
}
}
else {
ret = xmlStrdup(string);
}
}
return ret;
}
/**
* internal helper: insert node to nodelist
* synopsis: xmlNodePtr insert_node_to_nodelist( leader, insertnode, followup );
* while leader and followup are allready list nodes. both may be NULL
* if leader is null the parents children will be reset
* if followup is null the parent last will be reset.
* leader and followup has to be followups in the nodelist!!!
* the function returns the node inserted. if a fragment was inserted,
* the first node of the list will returned
*
* i ran into a misconception here. there should be a normalization function
* for the DOM, so sequences of text nodes can get replaced by a single
* text node. as i see DOM Level 1 does not allow text node sequences, while
* Level 2 and 3 do.
**/
xmlNodePtr
insert_node_to_nodelist( xmlNodePtr lead, xmlNodePtr node, xmlNodePtr follow ){
xmlNodePtr cld1, cld2, par;
if( node == NULL ) {
return;
}
cld1 = node;
cld2 = node;
par = NULL;
if( lead != NULL ) {
par = lead->parent;
}
else if( follow != NULL ) {
par = follow->parent;
}
if( node->type == XML_DOCUMENT_FRAG_NODE ) {
xmlNodePtr hn = node->children;
cld1 = node->children;
cld2 = node->last;
node->last = node->children = NULL;
while ( hn ) {
hn->parent = par;
hn = hn->next;
}
}
if( cld1 != NULL && cld2 != NULL && par != NULL ) {
cld1->parent = par;
cld2->parent = par;
if ( lead == NULL ) {
par->children = cld1;
}
else {
lead->next = cld1;
cld1->prev = lead;
}
if ( follow == NULL ){
par->last = cld2;
}
else {
follow->prev = cld2;
cld2->next = follow;
}
}
return cld1;
}
xmlNodePtr
domUnbindNode( xmlNodePtr );
xmlNodePtr
domSetOwnerDocument( xmlNodePtr self, xmlDocPtr newDoc );
xmlNodePtr
domIsNotParentOf( xmlNodePtr node1, xmlNodePtr node2 );
xmlNodePtr
domImportNode( xmlDocPtr doc, xmlNodePtr node, int move );
xmlNodePtr
domRemoveChild( xmlNodePtr self, xmlNodePtr old );
/**
* Name: domName
* Synopsis: string = domName( node );
*
* domName returns the full name for the current node.
* If the node belongs to a namespace it returns the prefix and
* the local name. otherwise only the local name is returned.
**/
xmlChar*
domName(xmlNodePtr node) {
xmlChar *qname = NULL;
if ( node ) {
if (node->ns != NULL && node->ns->prefix != NULL) {
xmlChar *tname = xmlStrdup( node->ns->prefix );
tname = xmlStrcat( tname , ":" );
tname = xmlStrcat( tname , node->name );
qname = tname;
}
else {
qname = xmlStrdup( node->name );
}
}
return qname;
}
void
domSetName( xmlNodePtr node, char* name ) {
xmlChar* str = NULL;
/* TODO: add ns support */
if ( node == NULL || name == NULL )
return ;
if ( node->name != NULL ) {
/* required since node->name is const! */
xmlFree( (void*) node->name );
}
node->name = xmlStrdup( name );
}
/**
* Name: domAppendChild
* Synopsis: xmlNodePtr domAppendChild( xmlNodePtr par, xmlNodePtr newCld );
* @par: the node to append to
* @newCld: the node to append
*
* Returns newCld on success otherwise NULL
* The function will unbind newCld first if nesseccary. As well the
* function will fail, if par or newCld is a Attribute Node OR if newCld
* is a parent of par.
*
* If newCld belongs to a different DOM the node will be imported
* implicit before it gets appended.
**/
xmlNodePtr
domAppendChild( xmlNodePtr self,
xmlNodePtr newChild ){
/* unbinds the new node if nessecary ... does not handle attributes :P */
/* fprintf( stderr,"check if child is not parent of the current node\n"); */
newChild = domIsNotParentOf( newChild, self );
if ( self == NULL ) {
return newChild;
}
if ( newChild == NULL
|| newChild->type == XML_ATTRIBUTE_NODE
|| self->type == XML_ATTRIBUTE_NODE
|| ( newChild->type == XML_DOCUMENT_FRAG_NODE
&& newChild->children == NULL )
){
/* HIERARCHIY_REQUEST_ERR */
/* fprintf(stderr,"HIERARCHIY_REQUEST_ERR\n"); */
return NULL;
}
if ( newChild->doc == self->doc ){
/* fprintf(stderr,"child part of the current dom\n"); */
newChild= domUnbindNode( newChild );
}
else {
/* WRONG_DOCUMENT_ERR - non conform implementation*/
/* fprintf(stderr,"WRONG_DOCUMENT_ERR - non conform implementation\n"); */
newChild= domImportNode( self->doc, newChild, 1 );
/* fprintf(stderr,"post import\n"); */
}
/* fprintf(stderr,"real append\n"); */
if ( self->children != NULL ) {
/* fprintf(stderr,"append to the end of the child list\n"); */
newChild = insert_node_to_nodelist( self->last, newChild , NULL );
}
else if (newChild->type == XML_DOCUMENT_FRAG_NODE ) {
xmlNodePtr cld = newChild->children;
/* fprintf(stderr," insert a fragment into an empty node\n"); */
self->children = newChild->children;
self->last = newChild->last;
while( cld != NULL ){
cld->parent = self;
cld = cld->next;
}
/* cld->parent = self; */
newChild->children = NULL;
newChild->last = NULL;
/* cld = self->children; */
}
else {
/* fprintf(stderr,"single node, no children\n"); */
self->children = newChild;
self->last = newChild;
newChild->parent= self;
}
/* fprintf(stderr,"append done...\n"); */
return newChild;
}
xmlNodePtr
domReplaceChild( xmlNodePtr self, xmlNodePtr new, xmlNodePtr old ) {
if ( self== NULL ){
return NULL;
}
if ( new == NULL ) {
/* level2 sais nothing about this case :( */
return domRemoveChild( self, old );
}
/* handle the different node types */
switch( new->type ) {
case XML_ATTRIBUTE_NODE:
return NULL;
break;
default:
break;
}
if( ( old != NULL
&& ( old->type == XML_ATTRIBUTE_NODE
|| old->type == XML_DOCUMENT_FRAG_NODE
|| old->parent != self ) )
|| self->type== XML_ATTRIBUTE_NODE
|| domIsNotParentOf( new, self ) == NULL
|| ( new->type == XML_DOCUMENT_FRAG_NODE && new->children == NULL ) ) {
/* HIERARCHY_REQUEST_ERR */
return NULL;
}
if ( new == old ) {
/* dom level 2 throws no exception if new and old are equal */
return new;
}
if ( old == NULL ) {
domAppendChild( self, new );
return old;
}
if ( new->doc == self->doc ) {
new = domUnbindNode( new ) ;
}
else {
/* WRONG_DOCUMENT_ERR - non conform implementation */
new = domImportNode( self->doc, new, 1 );
}
if( old == self->children && old == self->last ) {
domRemoveChild( self, old );
domAppendChild( self, new );
}
else {
insert_node_to_nodelist( old->prev, new, old->next );
old->parent = NULL;
old->next = NULL;
old->prev = NULL;
}
return old;
}
xmlNodePtr
domRemoveChild( xmlNodePtr self, xmlNodePtr old ) {
if ( (self != NULL)
&& (old!=NULL)
&& (self == old->parent )
&& (old->type != XML_ATTRIBUTE_NODE)
) {
old = domUnbindNode( old );
}
return old ;
}
xmlNodePtr
domInsertBefore( xmlNodePtr self,
xmlNodePtr newChild,
xmlNodePtr refChild ){
if ( self == NULL || newChild == NULL ) {
return NULL;
}
if ( newChild != NULL && domIsNotParentOf( newChild, self ) == NULL ){
/* HIERARCHIY_REQUEST_ERR */
return NULL;
}
if ( refChild == newChild ) {
return newChild;
}
if( ( refChild != NULL
&& ( refChild->type == XML_ATTRIBUTE_NODE
|| refChild->type == XML_DOCUMENT_FRAG_NODE ) )
|| ( newChild != NULL && ( newChild->type == XML_ATTRIBUTE_NODE
|| ( newChild->type==XML_DOCUMENT_FRAG_NODE
&& newChild->children == NULL ) ) ) ) {
/* HIERARCHY_REQUEST_ERR */
/* this condition is true, because:
* case 1: if the reference is an attribute, it's not a child
* case 2: if the reference is a document_fragment, it's not part of the tree
* case 3: if the newchild is an attribute, it can't get inserted as a child
* case 4: if newchild is a document fragment and has no children, we can't
* insert it
*/
return NULL;
}
if ( self->doc == newChild->doc ){
newChild = domUnbindNode( newChild );
}
else {
newChild = domImportNode( self->doc, newChild, 1 );
}
if ( refChild == NULL ) {
if( self->children == NULL ){
newChild = domAppendChild( self, newChild );
}
else {
newChild = insert_node_to_nodelist( NULL, newChild, self->children );
}
}
if ( self == refChild->parent ) {
newChild = insert_node_to_nodelist( refChild->prev, newChild, refChild );
}
else {
newChild = NULL;
}
return newChild;
}
xmlNodePtr
domInsertAfter( xmlNodePtr self,
xmlNodePtr newChild,
xmlNodePtr refChild ){
if ( self == NULL ) {
return NULL;
}
newChild = domIsNotParentOf( newChild, self );
if ( refChild == newChild ) {
return newChild;
}
if ( refChild == NULL ) {
return domAppendChild( self, newChild );
}
if( refChild->type == XML_ATTRIBUTE_NODE
|| refChild->type == XML_DOCUMENT_FRAG_NODE
|| ( newChild != NULL && ( newChild->type == XML_ATTRIBUTE_NODE
|| ( newChild->type==XML_DOCUMENT_FRAG_NODE
&& newChild->children == NULL ) ) ) ) {
/* HIERARCHY_REQUEST_ERR */
return NULL;
}
if ( newChild != NULL &&
self == refChild->parent &&
refChild != newChild ) {
if( newChild->doc == self->doc ) {
domUnbindNode( newChild );
}
else {
domImportNode( self->doc, newChild, 1 );
}
newChild = insert_node_to_nodelist( refChild, newChild, refChild->next );
}
else {
newChild = NULL;
}
return newChild;
}
xmlNodePtr
domReplaceNode( xmlNodePtr oldnode, xmlNodePtr newnode ){
xmlNodePtr prev, next, par;
if ( oldnode != NULL ) {
if( newnode == NULL ) {
domUnbindNode( oldnode );
}
else {
par = oldnode->parent;
prev = oldnode->prev;
next = oldnode->next;
domUnbindNode( oldnode );
if( prev == NULL && next == NULL ) {
domAppendChild( par ,newnode );
}
else {
insert_node_to_nodelist( prev, newnode, next );
}
}
}
return oldnode;
}
xmlChar*
domGetNodeValue( xmlNodePtr n ) {
xmlChar * retval = NULL;
if( n != NULL ) {
if ( n->type != XML_ATTRIBUTE_NODE ){
if ( n->content != NULL ) {
retval = xmlStrdup(n->content);
}
else {
if ( n->children != NULL ) {
xmlNodePtr cnode = n->children;
xs_warn ( "oh the node has children ..." );
/* ok then toString in this case ... */
while (cnode) {
xmlBufferPtr buffer = xmlBufferCreate();
/* buffer = xmlBufferCreate(); */
xmlNodeDump( buffer, n->doc, cnode, 0, 0 );
if ( buffer->content != NULL ) {
xs_warn( "add item" );
if ( retval != NULL ) {
retval = xmlStrcat( retval, buffer->content );
}
else {
retval = xmlStrdup( buffer->content );
}
}
xmlBufferFree( buffer );
cnode = cnode->next;
}
}
}
}
else if ( n->children != NULL ) {
xs_warn("copy kiddies content!");
retval = xmlStrdup(n->children->content);
}
else {
xs_warn( "no bloddy data!" );
}
}
return retval;
}
void
domSetNodeValue( xmlNodePtr n , xmlChar* val ){
if ( n == NULL )
return;
if ( val == NULL ){
val = "";
}
if( n->type == XML_ATTRIBUTE_NODE ){
if ( n->children != NULL ) {
n->last = NULL;
xmlFreeNodeList( n->children );
}
n->children = xmlNewText( val );
n->children->parent = n;
n->children->doc = n->doc;
n->last = n->children;
}
else if( n->content != NULL ) {
/* free old content */
xmlFree( n->content );
n->content = xmlStrdup(val);
}
}
void
domSetParentNode( xmlNodePtr self, xmlNodePtr p ) {
/* never set the parent to a node in the own subtree */
self = domIsNotParentOf( self, p );
if( self != NULL ){
if( self->parent != p ){
domUnbindNode( self );
self->parent = p;
if( p->doc != self->doc ) {
self->doc = p->doc;
}
}
}
}
xmlNodePtr
domUnbindNode( xmlNodePtr self ) {
if ( (self != NULL) && (self->parent != NULL) ) {
if( self->parent!=NULL ) {
if( self->parent->properties == (xmlAttrPtr) self )
self->parent->properties = (xmlAttrPtr)self->next;
if ( self == self->parent->last )
self->parent->last = self->prev;
if ( self == self->parent->children )
self->parent->children = self->next;
}
if ( self->next != NULL )
self->next->prev = self->prev;
if ( self->prev != NULL )
self->prev->next = self->next;
self->parent = NULL;
self->next = NULL;
self->prev = NULL;
}
return self;
}
/**
* donIsNotParentOf tests, if node1 is parent of node2. this test is very
* important to avoid circular constructs in trees. if node1 is NOT parent
* of node2 the function returns node1, otherwise NULL.
**/
xmlNodePtr
domIsNotParentOf( xmlNodePtr node1, xmlNodePtr node2 ) {
xmlNodePtr helper = NULL;
if ( node1 == NULL ) {
return NULL;
}
if( node2 == NULL || node1->doc != node2->doc) {
return node1;
}
if( node2->type == XML_DOCUMENT_NODE ){
return node1;
}
helper= node2;
while ( helper!=NULL ) {
if( helper == node1 ) {
return NULL;
}
helper = helper->parent;
if ( (xmlDocPtr) helper == node2->doc ) {
helper = NULL;
}
}
return node1;
}
/**
* this is a wrapper function that does the type evaluation for the
* node. this makes the code a little more readable in the .XS
*
* the code is not really portable, but i think we'll avoid some
* memory leak problems that way.
**/
const char*
domNodeTypeName( xmlNodePtr elem ){
const char *name = "XML::LibXML::Node";
if ( elem != NULL ) {
char * ptrHlp;
switch ( elem->type ) {
case XML_ELEMENT_NODE:
name = "XML::LibXML::Element";
break;
case XML_TEXT_NODE:
name = "XML::LibXML::Text";
break;
case XML_COMMENT_NODE:
name = "XML::LibXML::Comment";
break;
case XML_CDATA_SECTION_NODE:
name = "XML::LibXML::CDATASection";
break;
case XML_ATTRIBUTE_NODE:
name = "XML::LibXML::Attr";
break;
case XML_DOCUMENT_NODE:
name = "XML::LibXML::Document";
break;
case XML_DOCUMENT_FRAG_NODE:
name = "XML::LibXML::DocumentFragment";
break;
case XML_NAMESPACE_DECL:
name = "XML::LibXML::Namespace";
break;
case XML_DTD_NODE:
name = "XML::LibXML::Dtd";
break;
case XML_PI_NODE:
name = "XML::LibXML::PI";
break;
default:
name = "XML::LibXML::Node";
break;
};
return name;
}
return "";
}
xmlNodePtr
domCreateCDATASection( xmlDocPtr self , xmlChar * strNodeContent ){
xmlNodePtr elem = NULL;
if ( ( self != NULL ) && ( strNodeContent != NULL ) ) {
elem = xmlNewCDataBlock( self, strNodeContent, xmlStrlen(strNodeContent) );
elem->next = NULL;
elem->prev = NULL;
elem->children = NULL ;
elem->last = NULL;
elem->doc = self->doc;
}
return elem;
}
xmlNodePtr
domDocumentElement( xmlDocPtr doc ) {
xmlNodePtr cld=NULL;
if ( doc != NULL && doc->doc != NULL && doc->doc->children != NULL ) {
cld= doc->doc->children;
while ( cld != NULL && cld->type != XML_ELEMENT_NODE )
cld= cld->next;
}
return cld;
}
/**
* setDocumentElement:
* @doc: the document
* @newRoot: the new rootnode
*
* a document can have only ONE root node, so this function searches
* the first element and relaces this element with newRoot.
*
* Returns the old root node.
**/
xmlNodePtr
domSetDocumentElement( xmlDocPtr doc, xmlNodePtr newRoot ) {
return domReplaceChild( (xmlNodePtr)doc,
newRoot,
domDocumentElement( doc )) ;
}
xmlNodePtr
domImportNode( xmlDocPtr doc, xmlNodePtr node, int move ) {
xmlNodePtr return_node = node;
if ( node && node->doc != doc ) {
if ( move ) {
return_node = domUnbindNode( node );
}
else {
return_node = xmlCopyNode( node, 1 );
}
/* tell all children about the new boss */
return_node = domSetOwnerDocument( return_node, doc );
}
return return_node;
}
xmlNodeSetPtr
domGetElementsByTagName( xmlNodePtr n, xmlChar* name ){
xmlNodeSetPtr rv = NULL;
xmlNodePtr cld = NULL;
if ( n != NULL && name != NULL ) {
cld = n->children;
while ( cld != NULL ) {
if ( xmlStrcmp( name, cld->name ) == 0 ){
if ( rv == NULL ) {
rv = xmlXPathNodeSetCreate( cld ) ;
}
else {
xmlXPathNodeSetAdd( rv, cld );
}
}
cld = cld->next;
}
}
return rv;
}
xmlNodeSetPtr
domGetElementsByTagNameNS( xmlNodePtr n, xmlChar* nsURI, xmlChar* name ){
xmlNodeSetPtr rv = NULL;
if ( nsURI == NULL ) {
return domGetElementsByTagName( n, name );
}
if ( n != NULL && name != NULL ) {
xmlNodePtr cld = n->children;
while ( cld != NULL ) {
if ( xmlStrcmp( name, cld->name ) == 0
&& cld->ns != NULL
&& xmlStrcmp( nsURI, cld->ns->href ) == 0 ){
if ( rv == NULL ) {
rv = xmlXPathNodeSetCreate( cld ) ;
}
else {
xmlXPathNodeSetAdd( rv, cld );
}
}
cld = cld->next;
}
}
return rv;
}
xmlNodePtr
domSetOwnerDocument( xmlNodePtr self, xmlDocPtr newDoc ) {
if ( self != NULL ) {
xmlNodePtr cNode = self->children;
xmlNodePtr pNode = (xmlNodePtr)self->properties;
self->doc = newDoc;
while ( cNode != NULL ) {
domSetOwnerDocument( cNode, newDoc );
cNode = cNode->next;
}
while ( pNode != NULL ) {
domSetOwnerDocument( cNode, newDoc );
pNode = pNode->next;
}
}
return self;
}
xmlNsPtr
domNewNs ( xmlNodePtr elem , xmlChar *prefix, xmlChar *href ) {
xmlNsPtr ns = NULL;
if (elem != NULL) {
ns = xmlSearchNs( elem->doc, elem, prefix );
}
/* prefix is not in use */
if (ns == NULL) {
ns = xmlNewNs( elem , href , prefix );
} else {
/* prefix is in use; if it has same URI, let it go, otherwise it's
an error */
if (!xmlStrEqual(href, ns->href)) {
ns = NULL;
}
}
return ns;
}
/* This routine may or may not make it into libxml2; Matt wanted it in
here to be nice to those with older libxml2 installations.
This instance is renamed from xmlHasNsProp to domHasNsProp. */
/**
* xmlHasNsProp:
* @node: the node
* @name: the attribute name
* @namespace: the URI of the namespace
*
* Search for an attribute associated to a node
* This attribute has to be anchored in the namespace specified.
* This does the entity substitution.
* This function looks in DTD attribute declaration for #FIXED or
* default declaration values unless DTD use has been turned off.
*
* Returns the attribute or the attribute declaration or NULL
* if neither was found.
*/
xmlAttrPtr
domHasNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *namespace) {
xmlAttrPtr prop;
xmlDocPtr doc;
xmlNsPtr ns;
if (node == NULL)
return(NULL);
prop = node->properties;
if (namespace == NULL)
return(xmlHasProp(node, name));
while (prop != NULL) {
/*
* One need to have
* - same attribute names
* - and the attribute carrying that namespace
* or
SJT: This following condition is wrong IMHO; I reported it as a bug on libxml2
* no namespace on the attribute and the element carrying it
*/
if ((xmlStrEqual(prop->name, name)) &&
(/* ((prop->ns == NULL) && (node->ns != NULL) &&
(xmlStrEqual(node->ns->href, namespace))) || */
((prop->ns != NULL) &&
(xmlStrEqual(prop->ns->href, namespace))))) {
return(prop);
}
prop = prop->next;
}
#if 0
/* xmlCheckDTD is static in libxml/tree.c; it is set there to 1
and never changed, so commenting this out doesn't change the
behaviour */
if (!xmlCheckDTD) return(NULL);
#endif
/*
* Check if there is a default declaration in the internal
* or external subsets
*/
doc = node->doc;
if (doc != NULL) {
if (doc->intSubset != NULL) {
xmlAttributePtr attrDecl;
attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
if ((attrDecl == NULL) && (doc->extSubset != NULL))
attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
/*
* The DTD declaration only allows a prefix search
*/
ns = xmlSearchNs(doc, node, attrDecl->prefix);
if ((ns != NULL) && (xmlStrEqual(ns->href, namespace)))
return((xmlAttrPtr) attrDecl);
}
}
}
return(NULL);
}
xmlAttrPtr
domSetAttributeNode( xmlNodePtr node, xmlAttrPtr attr ) {
if ( attr != NULL && attr->type != XML_ATTRIBUTE_NODE )
return NULL;
if ( node == NULL || attr == NULL ) {
return attr;
}
if ( node == attr->parent ) {
return attr; /* attribute is allready part of the node */
}
if ( attr->doc != node->doc ){
attr = (xmlAttrPtr) domImportNode( node->doc, (xmlNodePtr) attr, 1 );
}
else {
attr = (xmlAttrPtr)domUnbindNode( (xmlNodePtr) attr );
}
/* stolen from libxml2 */
if ( attr != NULL ) {
if (node->properties == NULL) {
node->properties = attr;
} else {
xmlAttrPtr prev = node->properties;
while (prev->next != NULL) prev = prev->next;
prev->next = attr;
attr->prev = prev;
}
}
return attr;
}