]> git.sesse.net Git - xml-template/blobdiff - perl-sax/XML/TemplateSAX.pm
Add a note about non-reentrancy.
[xml-template] / perl-sax / XML / TemplateSAX.pm
index d2eb7a5bc1ead16e97b04f909f5a97b34826909c..e5e6e400fe629eca73edf539c65decdcd289c486 100644 (file)
@@ -3,7 +3,7 @@
 #
 # SAX version of XML::Template. Advantages over DOM: Doesn't have to load
 # the entire thing into memory, and you can chain filters. Disadvantages:
-# Slightly kludgier interface.
+# Slightly kludgier interface, and you won't get a DOM tree out.
 #
 # Differences from the DOM version:
 # 
 #   in the stream, usually between a parser and a writer (ie.
 #   parser -> XML::TemplateSAX::Handler -> writer). process_file works as
 #   before, but it returns a _string_, not a DOM tree.
-# - You can no longer insert a DOM tree. Instead, what you have is -- FIXME:
-#   figure out this :-)
+# - You can no longer insert a DOM tree, naturally. Instead, you can set up
+#   an XML::TemplateSAX::Buffer, let it gobble up your data, and send it
+#   in the way you'd insert a DOM tree. process_file_to_buffer does this
+#   transparently for you, returning a buffer you can give in. (In theory,
+#   one could avoid the buffering and just defer the parsing/filtering until
+#   it's needed, but Expat seems non-reentrant, which means starting a parser
+#   from within a begin_element callback blows up.)
 #
 
+use strict;
+use warnings;
 use XML::SAX::Expat;
 use XML::SAX::Writer;
-use Data::Dumper;
-
-package XML::TemplateSAX::Handler;
-use base qw(XML::SAX::Base);
-
-sub new {
-       my $class = shift;
-       my %options = @_;
-
-       my $self = {
-               obj => $options{'Content'},
-               stack => [],
-               Handler => $options{'Handler'}
-       };
-       bless($self, $class);
-       return $self;
-}
+use XML::TemplateSAX::Buffer;
+use XML::TemplateSAX::Cleaner;
+use XML::TemplateSAX::Handler;
 
-sub start_element {
-       my ($self, $data) = @_;
-       my $obj = $self->{'obj'};
-
-       # within a replacement; just ignore everything  
-       return if (!defined($obj));
-
-       # substitution: see if this element matches anything. if so,
-       # descend down into the tree.
-       if (ref($obj) eq 'HASH') {
-               my $match = undef;
-               for my $key (keys %$obj) {
-                       if ($key =~ /^#(.*)$/) {
-                               if ($data->{'NamespaceURI'} eq 'http://template.sesse.net/' && $data->{'LocalName'} eq $1) {
-                                       $match = $obj->{$key};
-                                       last;
-                               }
-                       } else {
-                               if ($data->{'LocalName'} eq $key) {
-                                       $match = $obj->{$key};
-                                       last;
-                               }
-                       }
-               }
+package XML::TemplateSAX;
 
-               if (defined($match)) {
-                       $self->SUPER::start_element($data);
-
-                       push @{$self->{'stack'}}, [ $data->{'Name'}, $obj ];
-                       
-                       #
-                       # This is sort of ugly. We special-case replacement by outputting
-                       # the string immediately, and then just ignoring the rest of the
-                       # events until we get to the right end tag. It's not 100% technically
-                       # correct for the case where you replace an entire document by a
-                       # string, but that's nonsensical anyway.
-                       #
-                       if (!ref($match)) {
-                               $self->SUPER::characters({ Data => $match });
-                               $match = undef;
-                       }
-
-                       $self->{'obj'} = $match;
-                       return;
-               }
-       }
-       
-       $self->SUPER::start_element($data);
-}
+sub process_file_to_handler {
+       my ($filename, $handler, $obj, $clean) = @_;
+       $clean = 1 unless (defined($clean));
 
-sub characters {
-       my ($self, $data) = @_;
-       return if (!defined($self->{'obj'}));
-       $self->SUPER::characters($data);
-}
+       my ($cleaner, $filter, $parser);
+       my $str = '';
 
-sub end_element {
-       my ($self, $data) = @_;
-
-       my $stack = $self->{'stack'};
-       if (scalar @$stack > 0) {
-               my $top = $stack->[$#stack];
-               
-               if ($data->{'Name'} eq $top->[0]) {
-                       $self->SUPER::end_element($data);
-                       $self->{'obj'} = $top->[1];
-                       pop @$stack;
-                       return;
-               }
+       if ($clean) {
+               $cleaner = XML::TemplateSAX::Cleaner->new(Handler => $handler);
+               $filter = XML::TemplateSAX::Handler->new(Handler => $cleaner, Content => $obj);
+       } else {
+               $filter = XML::TemplateSAX::Handler->new(Handler => $handler, Content => $obj);
        }
-       
-       return if (!defined($self->{'obj'}));
 
-       $self->SUPER::end_element($data);
-}
+       # FIXME: hardcoding expat = not good?
+       $parser = XML::SAX::Expat->new(Handler => $filter);
+       $parser->parse_file($filename);
 
-package XML::TemplateSAX;
+       return $str;
+}
 
 sub process_file {
        my ($filename, $obj, $clean) = @_;
 
        my $str = '';
-
-       # FIXME: hardcoding expat = not good?
        my $writer = XML::SAX::Writer->new(Output => \$str);
-       my $filter = XML::TemplateSAX::Handler->new(Handler => $writer, Content => $obj);
-       my $parser = XML::SAX::Expat->new(Handler => $filter);
-       $parser->parse_file($filename);
+
+       process_file_to_handler($filename, $writer, $obj, $clean);
 
        return $str;
 }
 
+sub process_file_to_buffer {
+       my ($filename, $obj, $clean) = @_;
+       
+       my $buffer = XML::TemplateSAX::Buffer->new;
+       process_file_to_handler($filename, $buffer, $obj, $clean);
+
+       return $buffer;
+}
+
+sub alternate {
+       my ($tag, $array, @elems) = @_;
+
+       my $i = 0;
+       my $num = scalar @elems;
+
+       for my $ref (@$array) {
+               if (defined($ref)) {
+                       $ref->{$tag} = $elems[$i++ % $num];
+               }
+       }
+
+       return $array;
+}
+
 1;