Commits

Shlomi Fish committed 56fffd8

Apply the NestedParsing patch from Daniel Frett.

- more of https://rt.cpan.org/Public/Bug/Display.html?id=4263
- Thanks to Daniel Frett for the patch.

[QUOTE]
Updated how legacy parser local callbacks are utilized by
init_callbacks so that the XML::LibXML::InputCallback object doesn't
have to be temporarily modified during the parsing process.

This change could break code for users that have subclassed
XML::LibXML::InputCallback and overridden the init_callbacks method
[/QUOTE]

Comments (0)

Files changed (4)

     - Apply the patch from Daniel Fetter's InputCallbackFix branch.
         - a partial fix to https://rt.cpan.org/Public/Bug/Display.html?id=4263 .
         - Call two $parser->parse_string() in succession.
+    - Apply the NestedParsing patch.
+        - more of https://rt.cpan.org/Public/Bug/Display.html?id=4263
+        - Thanks to Daniel Frett for the patch.
+
+        [QUOTE]
+        Updated how legacy parser local callbacks are utilized by
+        init_callbacks so that the XML::LibXML::InputCallback object doesn't
+        have to be temporarily modified during the parsing process.
+    
+        This change could break code for users that have subclassed
+        XML::LibXML::InputCallback and overridden the init_callbacks method
+        [/QUOTE]
+
 
 1.72            Thu Jun 16 19:26:13 IDT 2011
     - Removed a stray file from the MANIFEST
         $icb = $self->{XML_LIBXML_CALLBACK_STACK};
     }
 
-    my $mcb = $self->match_callback();
-    my $ocb = $self->open_callback();
-    my $rcb = $self->read_callback();
-    my $ccb = $self->close_callback();
-
-    if ( defined $mcb and defined $ocb and defined $rcb and defined $ccb ) {
-        $icb->register_callbacks( [$mcb, $ocb, $rcb, $ccb] );
-    }
-    $icb->init_callbacks();
+    $icb->init_callbacks($self);
 }
 
 sub _cleanup_callbacks {
     my $self = shift;
     $self->{XML_LIBXML_CALLBACK_STACK}->cleanup_callbacks();
-
-    my $mcb = $self->match_callback();
-    my $ocb = $self->open_callback();
-    my $rcb = $self->read_callback();
-    my $ccb = $self->close_callback();
-
-    if ( defined $mcb and defined $ocb and defined $rcb and defined $ccb ) {
-        $self->{XML_LIBXML_CALLBACK_STACK}->unregister_callbacks( [$mcb, $ocb, $rcb, $ccb] );
-    }
 }
 
 sub __read {
 #-------------------------------------------------------------------------#
 package XML::LibXML::InputCallback;
 
-use vars qw($_CUR_CB @_GLOBAL_CALLBACKS @_CB_STACK);
+use vars qw($_CUR_CB @_GLOBAL_CALLBACKS @_CB_STACK $_CB_NESTED_DEPTH @_CB_NESTED_STACK);
 
 BEGIN {
   $_CUR_CB = undef;
   @_GLOBAL_CALLBACKS = ();
   @_CB_STACK = ();
+  $_CB_NESTED_DEPTH = 0;
+  @_CB_NESTED_STACK = ();
 }
 
 sub CLONE_SKIP {
 # make libxml2 use the callbacks 
 sub init_callbacks {
     my $self = shift;
+    my $parser = shift;
 
+    #initialize the libxml2 callbacks unless this is a nested callback
+    $self->lib_init_callbacks() unless($_CB_NESTED_DEPTH);
+
+    #store the callbacks for any outer executing parser instance
+    $_CB_NESTED_DEPTH++;
+    push @_CB_NESTED_STACK, [
+      $_CUR_CB,
+      [@_CB_STACK],
+      [@_GLOBAL_CALLBACKS],
+    ];
+
+    #initialize the callback variables for the current parser
     $_CUR_CB           = undef;
     @_CB_STACK         = ();
-
     @_GLOBAL_CALLBACKS = @{ $self->{_CALLBACKS} };
     
+    #attach parser specific callbacks
+    if($parser) {
+        my $mcb = $parser->match_callback();
+        my $ocb = $parser->open_callback();
+        my $rcb = $parser->read_callback();
+        my $ccb = $parser->close_callback();
+        if ( defined $mcb and defined $ocb and defined $rcb and defined $ccb ) {
+            unshift @_GLOBAL_CALLBACKS, [$mcb, $ocb, $rcb, $ccb];
+        }
+    }
+
+    #attach global callbacks
     if ( defined $XML::LibXML::match_cb and 
          defined $XML::LibXML::open_cb  and 
          defined $XML::LibXML::read_cb  and 
                                   $XML::LibXML::read_cb,
                                   $XML::LibXML::close_cb];
     }
-
-    $self->lib_init_callbacks();
 }
 
 # reset libxml2's callbacks
 sub cleanup_callbacks {
     my $self = shift;
-    
-    $_CUR_CB           = undef;
-    @_GLOBAL_CALLBACKS = ();
-    @_CB_STACK         = ();
 
-    $self->lib_cleanup_callbacks();
+    #restore the callbacks for the outer parser instance
+    $_CB_NESTED_DEPTH--;
+    my $saved          = pop @_CB_NESTED_STACK;
+    $_CUR_CB           = $saved->[0];
+    @_CB_STACK         = (@{$saved->[1]});
+    @_GLOBAL_CALLBACKS = (@{$saved->[2]});
+
+    #clean up the libxml2 callbacks unless there are still outer parsing instances
+    $self->lib_cleanup_callbacks() unless($_CB_NESTED_DEPTH);
 }
 
 $XML::LibXML::__loaded=1;
                     </varlistentry>
 
                     <varlistentry>
-                        <term>init_callbacks()</term>
+                        <term>init_callbacks( $parser )</term>
 
                         <listitem>
-                            <para>Initializes the callback system before a parsing process.</para>
+                            <para>Initializes the callback system for the provided parser before starting a parsing process.</para>
                         </listitem>
                     </varlistentry>
 

t/28new_callbacks_multiple.t

 # $Id$
 use Test;
-BEGIN { plan tests => 70 }
+BEGIN { plan tests => 77 }
 END { ok(0) unless $loaded }
 use XML::LibXML;
 use IO::File;
 <x xmlns:xinclude="http://www.w3.org/2001/XInclude">
 <xml>test
 <xinclude:include href="/example/test2.xml"/>
-<xinclude:include href="/libxml/test2.xml"/></xml>
+<xinclude:include href="/libxml/test2.xml"/>
+<xinclude:include href="/xmldom/test2.xml"/></xml>
 </x>
 EOF
 
         $icb->register_callbacks( [ \&match_hash, \&open_hash, 
                                     \&read_hash, \&close_hash ] );
 
+        $icb->register_callbacks( [ \&match_xml, \&open_xml,
+                                    \&read_xml, \&close_xml ] );
+
 
         my $parser = XML::LibXML->new();
         $parser->expand_xinclude(1);
         my $doc = $parser->parse_string($string);
 
         ok($doc);
-        ok($doc->string_value(), "\ntest\n..\nbar..\n");
+        ok($doc->string_value(), "\ntest\n..\nbar..\nbarbar\n");
 }
 
 {
         $parser->expand_xinclude(1);
         $parser->input_callbacks($icb);
         my $doc = $parser->parse_string($string);
-        my $doc2 = $parser->parse_string($string);
 
         ok($doc);
         ok($doc->string_value(), "\ntest\nbar..\nbar..\n");
-        ok($doc2);
-        ok($doc2->string_value(), "\ntest\nbar..\nbar..\n");
         print $doc->serialize();
 
         $icb->unregister_callbacks( [ \&match_hash2, \&open_hash, 
         ok($doc->string_value(), "\ntest\n..\n\n         \n   \n");
 }
 
+{
+        my $string = <<EOF;
+<x xmlns:xinclude="http://www.w3.org/2001/XInclude">
+<xml>test
+<xinclude:include href="/example/test2.xml"/>
+<xinclude:include href="/xmldom/test2.xml"/></xml>
+</x>
+EOF
+        my $string2 = <<EOF;
+<x xmlns:xinclude="http://www.w3.org/2001/XInclude">
+<tmp/><xml>foo..<xinclude:include href="/example/test2.xml"/>bar</xml>
+</x>
+EOF
+
+
+        my $icb = XML::LibXML::InputCallback->new();
+        ok($icb);
+
+        my $open_xml2 = sub {
+                my $uri = shift;
+                my $parser = XML::LibXML->new;
+                $parser->expand_xinclude(1);
+                $parser->input_callbacks($icb);
+
+                my $dom = $parser->parse_string($string2);
+                ok($dom);
+        
+                return $dom;
+        };
+
+        $icb->register_callbacks( [ \&match_xml, $open_xml2,
+                                    \&read_xml, \&close_xml ] );
+
+        $icb->register_callbacks( [ \&match_hash2, \&open_hash,
+                                    \&read_hash, \&close_hash ] );
+
+        my $parser = XML::LibXML->new();
+        $parser->expand_xinclude(1);
+
+        $parser->match_callback( \&match_file );
+        $parser->open_callback( \&open_file );
+        $parser->read_callback( \&read_file );
+        $parser->close_callback( \&close_file );
+
+        $parser->input_callbacks($icb);
+
+        my $doc = $parser->parse_string($string);
+
+        ok($doc);
+        ok($doc->string_value(), "\ntest\n..\n\nfoo..bar..bar\n\n");
+}
+
 
 # --------------------------------------------------------------------- #
 # CALLBACKS
                 return 1;
         }
 }
+
+# --------------------------------------------------------------------- #
+# callback set 4 (perl xml reader)
+# --------------------------------------------------------------------- #
+sub match_xml {
+        my $uri = shift;
+        if ( $uri =~ /^\/xmldom\// ){
+                ok(1);
+                return 1;
+        }
+}
+
+sub open_xml {
+        my $uri = shift;
+        my $dom = XML::LibXML->new->parse_string(q{<?xml version="1.0"?><foo><tmp/>barbar</foo>});
+        ok($dom);
+
+        return $dom;
+}
+
+sub read_xml {
+        my $dom   = shift;
+        my $buflen = shift;
+
+        my $tmp = $dom->documentElement->findnodes('tmp')->shift;
+        my $rv = $tmp ? $dom->toString : "";
+        $tmp->unbindNode if($tmp);
+
+        ok(1);
+        return $rv;
+}
+
+sub close_xml {
+        my $dom   = shift;
+        undef $dom;
+        ok(1);
+}