Anonymous avatar Anonymous committed 6ba5802

- bugfix: getElementsByNodeName should work with a default prefix too
- improved setNamespaceDeclURI and setNamespaceDeclPrefix
- skip threads test unless env THREAD_TEST is set
- memory and threads tests give reason if skipped

Comments (0)

Files changed (9)

 use XML::LibXML::Common qw(:encoding :libxml);
 
 use XML::LibXML::NodeList;
+use XML::LibXML::XPathContext;
 use IO::Handle; # for FH reads called as methods
 
 
 
 sub getElementsByTagName {
     my ( $node , $name ) = @_;
-    my $xpath = "descendant::$name";
+    my $xpath = $name eq '*' ? "descendant::*" : "descendant::*[name()='$name']";
     my @nodes = $node->_findnodes($xpath);
     return wantarray ? @nodes : XML::LibXML::NodeList->new_from_ref(\@nodes, 1);
 }
     xmlSetGenericErrorFunc((void *) saved_error, (xmlGenericErrorFunc) LibXML_error_handler_ctx);
 }
 
+static int
+LibXML_will_die_ctx(SV * saved_error, int recover)
+{
+    if( 0 < SvCUR( saved_error ) ) {
+	if ( recover == 0 ) {
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+
 static void
 LibXML_report_error_ctx(SV * saved_error, int recover)
 {
-	if( 0 < SvCUR( saved_error ) ) {
-		if( recover ) {
-                   if ( recover == 1 ) {
-			warn("%s", SvPV_nolen(saved_error));
-                   } /* else recover silently */
-		} else {
-			croak("%s", SvPV_nolen(saved_error));
-		}
+    if( 0 < SvCUR( saved_error ) ) {
+	if( recover ) {
+	    if ( recover == 1 ) {
+		warn("%s", SvPV_nolen(saved_error));
+	    } /* else recover silently */
+	} else {
+	    croak("%s", SvPV_nolen(saved_error));
 	}
+    }
 }
 
 static int
             } else {
                 real_doc->URL = xmlStrdup((const xmlChar*)directory);
             }
-            if ( recover || ( well_formed &&
+            if ( ! LibXML_will_die_ctx(saved_error, recover) &&
+		 (recover || ( well_formed &&
                               ( !xmlDoValidityCheckingDefaultValue
                                 || ( valid || ( real_doc->intSubset == NULL
-                                                && real_doc->extSubset == NULL ))))) {
+                                                && real_doc->extSubset == NULL )))))) {
                 RETVAL = LibXML_NodeToSv( real_obj, (xmlNodePtr) real_doc );
             } else {
                 xmlFreeDoc(real_doc);
         prefix = nodeSv2C( svprefix , self );
         nsURI = nodeSv2C( newURI , self );
         /* null empty values */
-        if ( prefix != NULL && xmlStrlen(prefix) == 0) {
+        if ( prefix && xmlStrlen(prefix) == 0) {
             xmlFree( prefix );
             prefix = NULL;
         }
-        /* only allow xmlns="" with empty prefix */
-        if ( prefix != NULL && nsURI != NULL && xmlStrlen(nsURI) == 0) {
+        if ( nsURI && xmlStrlen(nsURI) == 0) {
 	     xmlFree( nsURI );
 	     nsURI = NULL;
         }
         RETVAL = 0;
         ns = self->nsDef;
-        while ( ns != NULL ) {
-		if ((ns->prefix != NULL || ns->href != NULL) &&
+        while ( ns ) {
+		if ((ns->prefix || ns->href ) &&
 		    ( xmlStrcmp( ns->prefix, prefix ) == 0 )) {
-			  if (ns->href != NULL) xmlFree((char*)ns->href);
+			  if (ns->href) xmlFree((char*)ns->href);
 			  ns->href = nsURI;
-			  nsURI = NULL; /* do not free it */
+			  if ( nsURI == NULL ) {
+			      domRemoveNsRefs( self, ns );
+			  } else
+			      nsURI = NULL; /* do not free it */
 			  RETVAL = 1;
-			  ns = NULL; /* end loop */
+			  break;
 		} else {
 		    ns = ns->next;
 		}
 	  }
-        if ( prefix != NULL) xmlFree( prefix );
-        if ( nsURI != NULL ) xmlFree( nsURI );
+        if ( prefix ) xmlFree( prefix );
+        if ( nsURI ) xmlFree( nsURI );
     OUTPUT:
         RETVAL
 
 		while ( ns != NULL ) {
 		    if ((ns->prefix != NULL || ns->href != NULL) &&
 			  xmlStrcmp( ns->prefix, prefix ) == 0 ) {
+			  if ( ns->href == NULL && nsPrefix != NULL ) {
+			      /* xmlns:foo="" - no go */
+			      if ( prefix != NULL) xmlFree(prefix);
+			      croak("setNamespaceDeclPrefix: cannot set non-empty prefix for empty namespace");
+			  }
 			  if ( ns->prefix != NULL ) xmlFree( ns->prefix );
 			  ns->prefix = nsPrefix;
 			  nsPrefix = NULL; /* do not free it */
 			  RETVAL = 1;
-			  ns = NULL; /* end loop */
+			  break;
 		    } else {
 			  ns = ns->next;
 		    }
             xmlFree(nsURI);
             nsURI = NULL;
         }
-        if ( (ns = xmlSearchNsByHref(node->doc, node, nsURI)) ) {
+        if ( nsPrefix == NULL && nsURI == NULL ) {
+	    /* special case: empty namespace */
+	    if ( (ns = xmlSearchNs(node->doc, node, NULL)) &&
+		 ( ns->href && xmlStrlen( ns->href ) != 0 ) ) {
+		/* won't take it */
+		RETVAL = 0;
+	    } else if ( flag ) {
+		/* no namespace */
+		xmlSetNs(node, NULL);
+		RETVAL = 1;
+	    } else {
+		RETVAL = 0;
+	    }
+	}
+        else if ( (ns = xmlSearchNsByHref(node->doc, node, nsURI)) ) {
             if ( ns->prefix == nsPrefix               /* both are NULL then */
                  || xmlStrEqual( ns->prefix, nsPrefix ) ) {
                 RETVAL = 1;
         if ( flag && ns ) {
             xmlSetNs(node, ns);
         }
-
-        xmlFree(nsPrefix);
-        xmlFree(nsURI);
+        if ( nsPrefix ) xmlFree(nsPrefix);
+        if ( nsURI ) xmlFree(nsURI);
     OUTPUT:
         RETVAL
 
+
 SV*
 _getNamespaceDeclURI( self, ns_prefix )
         xmlNodePtr self
         while ( ns != NULL ) {
 		if ( (ns->prefix != NULL || ns->href != NULL) &&
 		     xmlStrcmp( ns->prefix, prefix ) == 0 ) {
-                RETVAL = C2Sv(ns->href, NULL);
-		    ns = NULL;
+		    RETVAL = C2Sv(ns->href, NULL);
+		    break;
 		} else {
 		    ns = ns->next;
 		}
 
         /* NOTE: xmlIOParseDTD is documented to free its InputBuffer */
         xmlFree(new_string);
-
+        if ( res && LibXML_will_die_ctx(saved_error, 0) )
+	    xmlFreeDtd( res );
         LibXML_report_error_ctx(saved_error, 0);
         if (res == NULL) {
             croak("no DTD parsed!");
 	             the change.
            	  </para>
 	          <para>If the new URI is undef or empty, the nodes
-	             have no namespace after the change, but they
-	             retain their prefix. If the prefix is non-empty,
-                     it is recommended to move these nodes
-	             to the null namespace using
-	             e.g. <literal>setNamespace(undef,undef,1)</literal>
-	             in order to make the document namespace well-formed.
+	             have no namespace and no prefix after the change. 
 	             Namespace declarations
-	             that were once nulled in this way do not
+	             once nulled in this way do not
 	             further appear in the serialized output
-	             (but do actually remain in the document for internal integrity
+	             (but do remain in the document for internal integrity
 	             of libxml2 data structures).
                	  </para>
              	  <para>This function is NOT part of any DOM API.</para>
 	            <para>The function dies with an error
 	             if the element is in the scope of
 	             another declaration whose prefix equals
-	             to the new prefix. Otherwise,
-                     it returns 1 if the namespace declaration
+	             to the new prefix, or if the change should
+                     result in a declaration with a non-empty prefix but
+	             empty namespace URI.
+	             Otherwise, it returns 1 if the namespace declaration
 	             was found and changed and 0 if not found.</para>
 	            <para>All elements and attributes (even those previously
 	             unbound from the document) for which the
 domGetAttrNode(xmlNodePtr node, const xmlChar *qname) {
     xmlChar * prefix    = NULL;
     xmlChar * localname = NULL;
-    xmlAttrPtr * ret = NULL;
+    xmlAttrPtr ret = NULL;
     xmlNsPtr ns = NULL;
 
     if ( qname == NULL || node == NULL )
     return(1);
 }
 
+int
+domRemoveNsRefs(xmlNodePtr tree, xmlNsPtr ns) {
+    xmlAttrPtr attr;
+    xmlNodePtr node = tree;
+
+    if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) return(0);
+    while (node != NULL) {
+	if (node->ns == ns)
+	    node->ns = NULL; /* remove namespace reference */
+	attr = node->properties;
+	while (attr != NULL) {
+	    if (attr->ns == ns)
+		attr->ns = NULL; /* remove namespace reference */
+	    attr = attr->next;
+	}
+	/*
+	 * Browse the full subtree, deep first
+	 */
+	if (node->children != NULL && node->type != XML_ENTITY_REF_NODE) {
+	    /* deep first */
+	    node = node->children;
+	} else if ((node != tree) && (node->next != NULL)) {
+	    /* then siblings */
+	    node = node->next;
+	} else if (node != tree) {
+	    /* go up to parents->next if needed */
+	    while (node != tree) {
+		if (node->parent != NULL)
+		    node = node->parent;
+		if ((node != tree) && (node->next != NULL)) {
+		    node = node->next;
+		    break;
+		}
+		if (node->parent == NULL) {
+		    node = NULL;
+		    break;
+		}
+	    }
+	    /* exit condition */
+	    if (node == tree) 
+		node = NULL;
+	} else
+	    break;
+    }
+    return(1);
+}
 int
 domNodeNormalizeList( xmlNodePtr nodelist );
 
+int
+domRemoveNsRefs(xmlNodePtr tree, xmlNsPtr ns);
+
 #endif
+# -*- cperl -*-
 use Test;
-BEGIN { plan tests=>76; }
+BEGIN { plan tests=>96; }
 use XML::LibXML;
 use XML::LibXML::Common qw(:libxml);
 
     ok ( $root->setNamespaceDeclURI('','http://test') );	
     ok ( $root->lookupNamespaceURI(''), 'http://test' );
     ok ( $root->getNamespaceURI(), 'http://test' );
+
+    # changing prefix of the default ns declaration
     ok ( $root->setNamespaceDeclPrefix('','foo') );	
     ok ( $root->lookupNamespaceURI(''), undef );
     ok ( $root->lookupNamespaceURI('foo'), 'http://test' );
     ok ( $root->getNamespaceURI(),  'http://test' );
     ok ( $root->prefix(),  'foo' );
+
+    # turning a ns declaration to a default ns declaration
     ok ( $root->setNamespaceDeclPrefix('foo','') );	
     ok ( $root->lookupNamespaceURI('foo'), undef );
     ok ( $root->lookupNamespaceURI(''), 'http://test' );
     ok ( $root->getNamespaceURI(),  'http://test' );
     ok ( $root->prefix(),  undef );
 
+    # removing the default ns declaration
     ok ( $root->setNamespaceDeclURI('',undef) );
     ok ( $root->lookupNamespaceURI(''), undef );
     ok ( $root->getNamespaceURI(), undef );
     $strnode = $root->toString();
     ok ( $strnode !~ /xmlns=/ );
 
+    # namespaced attributes
+    $root->setAttribute('xxx:attr', 'value');
+    ok ( $root->getAttributeNode('xxx:attr') );
+    ok ( $root->getAttribute('xxx:attr'), 'value' );
+    ok ( $root->getAttributeNodeNS('http://example.com','attr') );
+    ok ( $root->getAttributeNS('http://example.com','attr'), 'value' );
+    ok ( $root->getAttributeNode('xxx:attr')->getNamespaceURI(), 'http://example.com');
 
-    # removing xmlns declarations (from output)
+    # removing other xmlns declarations
     $root->addNewChild('http://example.com', 'xxx:foo');
-    ok( $root->setNamespaceDeclURI('xxx',"") );	
+    ok( $root->setNamespaceDeclURI('xxx',undef) );	
     ok ( $root->lookupNamespaceURI('xxx'), undef );
     ok ( $root->getNamespaceURI(), undef );
     ok ( $root->firstChild->getNamespaceURI(), undef );
     ok ( $root->prefix(),  undef );
-    ok ( $root->firstChild->prefix(),  'xxx' );
+    ok ( $root->firstChild->prefix(),  undef );
+
+
+    # check namespaced attributes
+    ok ( $root->getAttributeNode('xxx:attr'), undef );
+    ok ( $root->getAttributeNodeNS('http://example.com', 'attr'), undef );
+    ok ( $root->getAttributeNode('attr') );
+    ok ( $root->getAttribute('attr'), 'value' );
+    ok ( $root->getAttributeNodeNS(undef,'attr') );
+    ok ( $root->getAttributeNS(undef,'attr'), 'value' );
+    ok ( $root->getAttributeNode('attr')->getNamespaceURI(), undef);
+
 
     $strnode = $root->toString();
     ok ( $strnode !~ /xmlns=/ );
     ok ( $strnode !~ /xmlns:xxx=/ );
-    ok ( $strnode =~ /<xxx:foo/ );
-    ok ( $doc->findnodes('/document/foo')->size() == 1 );
+    ok ( $strnode =~ /<foo/ );
+    
+    ok ( $root->setNamespaceDeclPrefix('xxx',undef) );
 
-    # the document still contains <xxx:foo>.
-    # this is how to get rid of the prefix
-    $root->firstChild->setNamespace(undef,undef,1);
+    ok ( $doc->findnodes('/document/foo')->size(), 1 );
+    ok ( $doc->findnodes('/document[foo]')->size(), 1 );
+    ok ( $doc->findnodes('/*[*]')->size(), 1 );
+
+    $xp = XML::LibXML::XPathContext->new($doc);
+    ok ( $xp->findnodes('/document/foo')->size(), 1 );
+    ok ( $xp->findnodes('/document[foo]')->size(), 1 );
+    ok ( $xp->findnodes('/document[@attr and foo]')->size(), 1 );
+    ok ( $xp->findvalue('/document/@attr'), 'value' );
+    ok ( $xp->findnodes('/document[foo]')->size(), 1 );
+
     ok ( $root->firstChild->prefix(),  undef );
 }
 use Test;
+use constant PLAN => 26;
+use constant TIMES_THROUGH => $ENV{MEMORY_TIMES} || 100_000;
 BEGIN { 
-    if ($^O eq 'linux' && $ENV{MEMORY_TEST}) {
-        plan tests => 26;
-    }
-    else {
-        plan tests => 0;
-        print "# Skipping test on this platform\n";
-    }
+    plan tests => PLAN;
+    if ($^O ne 'linux' ) {
+        skip "linux platform only\n" for 1..PLAN;
+    } elsif (not $ENV{MEMORY_TEST}) {
+        skip "developers only (set MEMORY_TEST=1 to run these tests)\n" for 1..PLAN;
+    }   
 }
 use XML::LibXML;
 use XML::LibXML::SAX::Builder;
     
         ok(1);
 
-        my $times_through = $ENV{MEMORY_TIMES} || 100_000;
-    
         print("# BASELINE\n");
         check_mem(1);
 
 
         # multiple parsers:
         print("# MULTIPLE PARSERS\n");
-        for (1..$times_through) {
+	XML::LibXML->new(); # first parser
+        check_mem(1);
+	
+        for (1..TIMES_THROUGH) {
             my $parser = XML::LibXML->new();
         }
         ok(1);
         check_mem();
         # multiple parses
         print("# MULTIPLE PARSES\n");
-        for (1..$times_through) {
+        for (1..TIMES_THROUGH) {
             my $parser = XML::LibXML->new();
             my $dom = $parser->parse_string("<sometag>foo</sometag>");
         }
 
         # multiple failing parses
         print("# MULTIPLE FAILURES\n");
-        for (1..$times_through) {
+        for (1..TIMES_THROUGH) {
             # warn("$_\n") unless $_ % 100;
             my $parser = XML::LibXML->new();
             eval {
         # building custom docs
         print("# CUSTOM DOCS\n");
         my $doc = XML::LibXML::Document->new();
-        for (1..$times_through)        {
+        for (1..TIMES_THROUGH)        {
             my $elem = $doc->createElement('x');
             
             if($peek) {
 
         {
             my $doc = XML::LibXML->createDocument;
-            for (1..$times_through)        {
+            for (1..TIMES_THROUGH)        {
                 make_doc2( $doc );
             }
         }
 
         ok($dtdstr);
 
-        for ( 1..$times_through ) {
+        for ( 1..TIMES_THROUGH ) {
             my $dtd = XML::LibXML::Dtd->parse_string($dtdstr);
         }
         ok(1);
 
         print( "# DTD URI parsing \n");
         # parse a DTD from a SYSTEM ID
-        for ( 1..$times_through ) {
+        for ( 1..TIMES_THROUGH ) {
             my $dtd = XML::LibXML::Dtd->new('ignore', 'example/test.dtd');
         }
         ok(1);
                 local $SIG{'__WARN__'} = sub { };
                 $xml = XML::LibXML->new->parse_file('example/article_bad.xml');
             };
-            for ( 1..$times_through ) {
+            for ( 1..TIMES_THROUGH ) {
                 my $good;
                 eval {
                     local $SIG{'__WARN__'} = sub { };
             check_mem();
         
             print "# validate() \n";
-            for ( 1..$times_through ) {
+            for ( 1..TIMES_THROUGH ) {
                 eval {
                     local $SIG{'__WARN__'} = sub { };
                     $xml->validate($dtd);
             # my $str = "<foo><bar><foo/></bar></foo>";
             my $str = $xml;
             my $doc = XML::LibXML->new->parse_string( $str );
-            for ( 1..$times_through ) {
+            for ( 1..TIMES_THROUGH ) {
                  processMessage($xml, '/dromedaries/species' );
 #                my @nodes = $doc->findnodes("/foo/bar/foo");
             }
         {
             my $str = "<foo><bar><foo/></bar></foo>";
             my $doc = XML::LibXML->new->parse_string( $str );
-            for ( 1..$times_through ) {
+            for ( 1..TIMES_THROUGH ) {
                 my $nodes = $doc->find("/foo/bar/foo");
             }
             ok(1);
 #            print "# ENCODING TESTS \n";
 #            my $string = "test � � is a test string to test iso encoding";
 #            my $encstr = encodeToUTF8( "iso-8859-1" , $string );
-#            for ( 1..$times_through ) {
+#            for ( 1..TIMES_THROUGH ) {
 #                my $str = encodeToUTF8( "iso-8859-1" , $string );
 #            }
 #            ok(1);
 #            check_mem();
 
-#            for ( 1..$times_through ) {
+#            for ( 1..TIMES_THROUGH ) {
 #                my $str = encodeToUTF8( "iso-8859-2" , "abc" );
 #            }
 #            ok(1);
 #            check_mem();
 #    
-#            for ( 1..$times_through ) {
+#            for ( 1..TIMES_THROUGH ) {
 #                my $str = decodeFromUTF8( "iso-8859-1" , $encstr );
 #            }
 #            ok(1);
 
             my $doc = XML::LibXML->new()->parse_string( $string );
 
-            for (1..$times_through) {
+            for (1..TIMES_THROUGH) {
                 my @ns = $doc->documentElement()->getNamespaces();
                 # warn "ns : " . $_->localname . "=>" . $_->href foreach @ns;
                 my $prefix = $_->localname foreach @ns;
        
             foreach my $key ( keys %xmlStrings )  {
                 print "# $key \n";
-                for (1..$times_through) {
+                for (1..TIMES_THROUGH) {
                     my $doc = $parser->parse_string( $xmlStrings{$key} );
                 }
 
        if(0) {
             foreach my $key ( keys %xmlStrings )  {
                 print "# $key \n";
-                for (1..$times_through) {
+                for (1..TIMES_THROUGH) {
                     map { $parser->push( $_ ) } @{$xmlStrings{$key}};
                     my $doc = $parser->finish_push();
                 }
             print "# BAD PUSHED DATA\n";
             foreach my $key ( "SIMPLE","SIMPLE2", "SIMPLE TEXT","SIMPLE CDATA","SIMPLE JUNK" )  {
                 print "# $key \n";
-                for (1..$times_through) {
+                for (1..TIMES_THROUGH) {
                     eval {map { $parser->push( $_ ) } @{$xmlBadStrings{$key}};};
                     eval {my $doc = $parser->finish_push();};
                 }
        
             foreach my $key ( keys %xmlStrings )  {
                 print "# $key \n";
-                for (1..$times_through) {
+                for (1..TIMES_THROUGH) {
                     eval {map { $parser->push( $_ ) } @{$xmlStrings{$key}};};
                     eval {my $doc = $parser->finish_push();};
                 }
 
             foreach my $key ( keys %xmlBadStrings )  {
                 print "# $key \n";
-                for (1..$times_through) {
+                for (1..TIMES_THROUGH) {
                     eval {map { $parser->push( $_ ) } @{$xmlBadStrings{$key}};};
                     eval {my $doc = $parser->finish_push();};
                 }
 use Config;
 use constant MAX_THREADS => 10;
 use constant MAX_LOOP => 50;
+use constant PLAN => 14;
 BEGIN {
-	if( $Config{useithreads} ) {
-		plan tests => 14;
-		require threads;
-	} else {
-		plan tests => 0;
-		exit;
-	}
+  plan tests => PLAN;
+  if( $Config{useithreads} ) {
+    if ($ENV{THREAD_TEST}) {
+      require threads;;
+    } else {
+      skip("optional (set THREAD_TEST=1 to run these tests)\n") for (1..PLAN);
+      exit;
+    }
+  } else {
+    skip("no ithreads in this Perl\n") for (1..PLAN);
+    exit;
+  }
 }
 use XML::LibXML;
 ok(1);
  * xpathcontext.h
  * 
  * This file is directly included into LibXML.xs.
- * It defines support routines for XML::LibXML::XPathContext
- * implementation.
  *
  */
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.