Commits

Anonymous committed 8356ff3

- get{Elements,Children}By{TagName,TagNameNS,LocalName} now
obey wildcards '*'
- getChildrenByLocalName was added
- extended test cases and documentation for these methods

Comments (0)

Files changed (5)

 Revision history for Perl extension XML::LibXML
 
+1.61pre 
+   - get{Elements,Children}By{TagName,TagNameNS,LocalName} now
+     obey wildcards '*', getChildrenByLocalName was added.
+
 1.60
    - getElementsById corrected to getElementById and the old name kept
      as an alias. Also re-implemented without XPath for improved
 # DOM L3 Document functions.
 # added after robins implicit feature requst
 #-------------------------------------------------------------------------#
-sub getElementsByTagName {
-    my ( $doc , $name ) = @_;
-    my $xpath = "descendant-or-self::node()/$name";
-    my @nodes = $doc->_findnodes($xpath);
-    return wantarray ? @nodes : XML::LibXML::NodeList->new(@nodes);
-}
-
-sub  getElementsByTagNameNS {
-    my ( $doc, $nsURI, $name ) = @_;
-    my $xpath = "descendant-or-self::*[local-name()='$name' and namespace-uri()='$nsURI']";
-    my @nodes = $doc->_findnodes($xpath);
-    return wantarray ? @nodes : XML::LibXML::NodeList->new(@nodes);
-}
-
-sub getElementsByLocalName {
-    my ( $doc,$name ) = @_;
-    my $xpath = "descendant-or-self::*[local-name()='$name']";
-    my @nodes = $doc->_findnodes($xpath);
-    return wantarray ? @nodes : XML::LibXML::NodeList->new(@nodes);
-}
+*getElementsByTagName = \&XML::LibXML::Element::getElementsByTagName;
+*getElementsByTagNameNS = \&XML::LibXML::Element::getElementsByTagNameNS;
+*getElementsByLocalName = \&XML::LibXML::Element::getElementsByLocalName;
 
 1;
 
 
 sub  getElementsByTagNameNS {
     my ( $node, $nsURI, $name ) = @_;
-    my $xpath = "descendant::*[local-name()='$name' and namespace-uri()='$nsURI']";
+    my $xpath;
+    if ( $name eq '*' ) {
+      if ( $nsURI eq '*' ) {
+	$xpath = "descendant::*";
+      } else {
+	$xpath = "descendant::*[namespace-uri()='$nsURI']";
+      }
+    } elsif ( $nsURI eq '*' ) {
+      $xpath = "descendant::*[local-name()='$name']";
+    } else {
+      $xpath = "descendant::*[local-name()='$name' and namespace-uri()='$nsURI']";
+    }
     my @nodes = $node->_findnodes($xpath);
     return wantarray ? @nodes : XML::LibXML::NodeList->new(@nodes);
 }
 
 sub getElementsByLocalName {
     my ( $node,$name ) = @_;
-    my $xpath = "descendant::*[local-name()='$name']";
-        my @nodes = $node->_findnodes($xpath);
+    my $xpath;
+    if ($name eq '*') {
+      $xpath = "descendant::*";
+    } else {
+      $xpath = "descendant::*[local-name()='$name']";
+    }
+    my @nodes = $node->_findnodes($xpath);
     return wantarray ? @nodes : XML::LibXML::NodeList->new(@nodes);
 }
 
 sub getChildrenByTagName {
     my ( $node, $name ) = @_;
-    my @nodes = grep { $_->nodeName eq $name } $node->childNodes();
+    my @nodes;
+    if ($name eq '*') {
+      @nodes = grep { $_->nodeType == XML::LibXML::XML_ELEMENT_NODE() }
+	$node->childNodes();
+    } else {
+      @nodes = grep { $_->nodeName eq $name } $node->childNodes();
+    }
+    return wantarray ? @nodes : XML::LibXML::NodeList->new(@nodes);
+}
+
+sub getChildrenByLocalName {
+    my ( $node, $name ) = @_;
+    my @nodes;
+    if ($name eq '*') {
+      @nodes = grep { $_->nodeType == XML::LibXML::XML_ELEMENT_NODE() }
+	$node->childNodes();
+    } else {
+      @nodes = grep { $_->nodeType == XML::LibXML::XML_ELEMENT_NODE() and
+		      $_->localName eq $name } $node->childNodes();
+    }
     return wantarray ? @nodes : XML::LibXML::NodeList->new(@nodes);
 }
 
         xmlAttrPtr attr;
     CODE:
         if ( id != NULL ) {
-            attr = xmlGetID(self, id);
+            attr = xmlGetID(self, (xmlChar *) id);
             if (attr == NULL)
                 elem = NULL;
             else if (attr->type == XML_ATTRIBUTE_NODE)
         xmlNodePtr cld;
         SV * element;
         int len = 0;
+	int name_wildcard = 0;
+	int ns_wildcard = 0;
         int wantarray = GIMME_V;
     PPCODE:
         name = nodeSv2C(node_name, self );
         nsURI = nodeSv2C(namespaceURI, self );
 
-        if ( nsURI != NULL && xmlStrlen(nsURI) == 0 ){
-            xmlFree(nsURI);
-            nsURI = NULL;
+        if ( nsURI != NULL ) { 
+            if (xmlStrlen(nsURI) == 0 ) {
+                xmlFree(nsURI);
+                nsURI = NULL;
+            } else if (xmlStrcmp( nsURI, (xmlChar *)"*" )==0) {
+                ns_wildcard = 1;	        
+            }
+        }
+        if ( name !=NULL && xmlStrcmp( name, (xmlChar *)"*" ) == 0) {
+            name_wildcard = 1;
         }
         if ( self->type != XML_ATTRIBUTE_NODE ) {
             cld = self->children;
             xs_warn("childnodes start");
             while ( cld ) {
-	      if ( xmlStrcmp( name, cld->name ) == 0 
-		   && ((cld->ns != NULL && xmlStrcmp( nsURI, cld->ns->href ) == 0 )
-		       || (cld->ns == NULL && nsURI == NULL))) {
+	      if ((name_wildcard && (cld->type == XML_ELEMENT_NODE) || 
+		   xmlStrcmp( name, cld->name ) == 0)
+		   && (ns_wildcard ||
+		       (cld->ns != NULL && 
+                        xmlStrcmp(nsURI,cld->ns->href) == 0 ) ||
+                       (cld->ns == NULL && nsURI == NULL))) {
                 if( wantarray != G_SCALAR ) {
                     element = PmmNodeToSv(cld, PmmOWNERPO(PmmPROXYNODE(self)) );
                     XPUSHs(sv_2mortal(element));
                         <funcsynopsisinfo>@nodes = $node-&#62;getChildrenByTagName($tagname);</funcsynopsisinfo>
                     </funcsynopsis>
 
-                    <para>The function gives direct access to all childnodes of the current node with the same tagname. It makes things a lot easier if you need
-                    to handle big datasets.</para>
-
-                    <para>If this function is called in SCALAR context, it returns the number of Elements found.</para>
+                    <para>The function gives direct access to all child elements of the current node with a given tagname, where
+	    tagname is a qualified name, that is, in case of namespace usage it may consist of a prefix and local
+	    name. This function makes things a lot easier if one needs
+                    to handle big datasets. A special tagname '*' can be used to match any name.</para>
+
+                    <para>If this function is called in SCALAR context, it returns the number of elements found.</para>
                 </listitem>
             </varlistentry>
         </variablelist>
                         <funcsynopsisinfo>@nodes = $node-&#62;getChildrenByTagNameNS($nsURI,$tagname);</funcsynopsisinfo>
                     </funcsynopsis>
 
-                    <para>Namespace version of <function>getChildrenByTagName</function>.</para>
-
-                    <para>If this function is called in SCALAR context, it returns the number of Elements found.</para>
+	  <para>Namespace version of <function>getChildrenByTagName</function>. A special nsURI '*' matches any namespace URI,
+	    in which case the function behaves just like <function>getChildrenByLocalName</function>.</para>
+
+                    <para>If this function is called in SCALAR context, it returns the number of elements found.</para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+
+        <variablelist>
+            <varlistentry>
+                <term>getChildrenByLocalName</term>
+
+                <listitem>
+                    <funcsynopsis>
+                        <funcsynopsisinfo>@nodes = $node-&#62;getChildrenByLocalName($localname);</funcsynopsisinfo>
+                    </funcsynopsis>
+
+                    <para>The function gives direct access to all child elements of the current node with a given local name. It makes things a lot easier if one needs
+	    to handle big datasets. A special <function>localname</function> '*' can be used to match any local name.</para>
+
+                    <para>If this function is called in SCALAR context, it returns the number of elements found.</para>
                 </listitem>
             </varlistentry>
         </variablelist>
                         <funcsynopsisinfo>@nodes = $node-&#62;getElementsByTagName($tagname);</funcsynopsisinfo>
                     </funcsynopsis>
 
-                    <para>This function is part of the spec it fetches all descendants of a node with a given tagname. If one is as confused with
-                    <function>tagname</function> as I was, tagname is a qualified tagname which is in case of namespace useage prefix and local name</para>
+                    <para>This function is part of the spec. It
+	    fetches all descendants of a node with a given tagname,
+	    where <function>tagname</function> is a qualified name,
+	    that is, in case of namespace usage it may consist of a prefix and
+	    local name.
+	    A special <function>tagname</function> '*' can be used to match any tag name.
+	  </para>
 
                     <para>In SCALAR context this function returns a <function>XML::LibXML::NodeList</function> object.</para>
                 </listitem>
                         <funcsynopsisinfo>@nodes = $node-&#62;getElementsByTagNameNS($nsURI,$localname);</funcsynopsisinfo>
                     </funcsynopsis>
 
-                    <para>Namespace version of <function>getElementsByTagName</function> as found in the DOM spec.</para>
+                    <para>Namespace version of <function>getElementsByTagName</function> as found in the DOM spec.
+               	    A special <function>localname</function> '*' can be used to match any local name
+	            and <function>nsURI</function> '*' can be used to match any namespace URI.</para>
 
                     <para>In SCALAR context this function returns a <function>XML::LibXML::NodeList</function> object.</para>
                 </listitem>
 use Test;
 use strict;
 
-BEGIN { plan tests => 114 };
+BEGIN { plan tests => 131 };
 use XML::LibXML;
 use XML::LibXML::Common qw(:libxml);
 
         my $string2 = '<C:A xmlns:C="xml://D"><C:A><C:B/></C:A><C:A><C:B/></C:A></C:A>';
         my $string3 = '<A xmlns="xml://D"><A><B/></A><A><B/></A></A>';
         my $string4 = '<C:A><C:A><C:B/></C:A><C:A><C:B/></C:A></C:A>';
+        my $string5 = '<A xmlns:C="xml://D"><C:A>foo<A/>bar</C:A><A><C:B/>X</A>baz</A>';
         {
             my $doc2 = $parser2->parse_string($string1);
             my @as   = $doc2->getElementsByTagName( "A" );
             ok( scalar( @as ), 3);
 
+            @as   = $doc2->getElementsByTagName( "*" );
+            ok( scalar( @as ), 5);
+
+            @as   = $doc2->getElementsByTagNameNS( "*", "B" );
+            ok( scalar( @as ), 2);
+
             @as   = $doc2->getElementsByLocalName( "A" );
             ok( scalar( @as ), 3);
+
+            @as   = $doc2->getElementsByLocalName( "*" );
+            ok( scalar( @as ), 5);
         }
         {
             my $doc2 = $parser2->parse_string($string2);
             ok( scalar( @as ), 3);
             @as   = $doc2->getElementsByTagNameNS( "xml://D", "A" );
             ok( scalar( @as ), 3);
+            @as   = $doc2->getElementsByTagNameNS( "*", "A" );
+            ok( scalar( @as ), 3);
             @as   = $doc2->getElementsByLocalName( "A" );
             ok( scalar( @as ), 3);
         }
             my @as   = $doc2->getElementsByLocalName( "A" );
             ok( scalar( @as ), 3);
         }
+        {
+            my $doc2 = $parser2->parse_string($string5);
+            my @as   = $doc2->getElementsByTagName( "C:A" );
+            ok( scalar( @as ), 1);
+            @as   = $doc2->getElementsByTagName( "A" );
+            ok( scalar( @as ), 3);
+            @as   = $doc2->getElementsByTagNameNS( "*", "A" );
+            ok( scalar( @as ), 4);
+            @as   = $doc2->getElementsByTagNameNS( "*", "*" );
+            ok( scalar( @as ), 5);
+            @as   = $doc2->getElementsByTagNameNS( "xml://D", "*" );
+            ok( scalar( @as ), 2);
+
+	    my $A = $doc2->getDocumentElement;
+            @as   = $A->getChildrenByTagName( "A" );
+	    ok( scalar( @as ), 1);
+            @as   = $A->getChildrenByTagName( "C:A" );
+	    ok( scalar( @as ), 1);
+            @as   = $A->getChildrenByTagName( "C:B" );
+	    ok( scalar( @as ), 0);
+            @as   = $A->getChildrenByTagName( "*" );
+	    ok( scalar( @as ), 2);
+            @as   = $A->getChildrenByTagNameNS( "*", "A" );
+	    ok( scalar( @as ), 2);
+            @as   = $A->getChildrenByTagNameNS( "xml://D", "*" );
+	    ok( scalar( @as ), 1);
+            @as   = $A->getChildrenByTagNameNS( "*", "*" );
+            ok( scalar( @as ), 2);
+            @as   = $A->getChildrenByLocalName( "A" );
+            ok( scalar( @as ), 2);
+        }
     }
 }