X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=perl-sax%2FXML%2FTemplateSAX.pm;h=e5e6e400fe629eca73edf539c65decdcd289c486;hb=c00c787ebcd86ec94285b708db96a20286e57ab0;hp=ff68c1f9c464b552c3a907d4b54c94b32b2f0fc5;hpb=7ad42bb3b510baa203bfc4126e0b1b3181f9e861;p=xml-template diff --git a/perl-sax/XML/TemplateSAX.pm b/perl-sax/XML/TemplateSAX.pm index ff68c1f..e5e6e40 100644 --- a/perl-sax/XML/TemplateSAX.pm +++ b/perl-sax/XML/TemplateSAX.pm @@ -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: # @@ -11,149 +11,79 @@ # 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'}; - - # find the ID, if any - my $id = $data->{'Attributes'}->{'{http://template.sesse.net/}id'}; - $id = $id->{'Value'} if (defined($id)); - - # 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 (defined($id) && $id 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); + + return $str; } -package XML::TemplateSAX::Cleaner; -use base qw(XML::SAX::Base); +sub process_file { + my ($filename, $obj, $clean) = @_; -sub start_element { - my ($self, $data) = @_; - my $attrs = $data->{'Attributes'}; + my $str = ''; + my $writer = XML::SAX::Writer->new(Output => \$str); - for my $a (keys %$attrs) { - if ($attrs->{$a}->{'NamespaceURI'} eq 'http://template.sesse.net/') { - delete $attrs->{$a}; - } - } + process_file_to_handler($filename, $writer, $obj, $clean); - $self->SUPER::start_element($data); + return $str; } -package XML::TemplateSAX; - -sub process_file { +sub process_file_to_buffer { my ($filename, $obj, $clean) = @_; - $clean = 1 unless (defined($clean)); + + my $buffer = XML::TemplateSAX::Buffer->new; + process_file_to_handler($filename, $buffer, $obj, $clean); - my ($writer, $cleaner, $filter, $parser); - my $str = ''; + return $buffer; +} - # FIXME: hardcoding expat = not good? - $writer = XML::SAX::Writer->new(Output => \$str); +sub alternate { + my ($tag, $array, @elems) = @_; - if ($clean) { - $cleaner = XML::TemplateSAX::Cleaner->new(Handler => $writer, Content => $obj); - $filter = XML::TemplateSAX::Handler->new(Handler => $cleaner, Content => $obj); - } else { - $filter = XML::TemplateSAX::Handler->new(Handler => $writer, Content => $obj); - } + my $i = 0; + my $num = scalar @elems; - $parser = XML::SAX::Expat->new(Handler => $filter); - $parser->parse_file($filename); + for my $ref (@$array) { + if (defined($ref)) { + $ref->{$tag} = $elems[$i++ % $num]; + } + } - return $str; + return $array; } 1;