7 package XML::TemplateSAX::Handler;
8 use base qw(XML::SAX::Base);
15 obj => $options{'Content'},
17 Handler => $options{'Handler'}
24 my ($self, $data) = @_;
25 my $obj = $self->{'obj'};
28 my $id = $data->{'Attributes'}->{'{http://template.sesse.net/}id'};
29 $id = $id->{'Value'} if (defined($id));
32 # If we have multiple elements with the same name within each other,
33 # we need to keep track of how many there are, so check here and increment.
34 # end_element will decrement and optionally pop the stack.
36 my $stack = $self->{'stack'};
37 if (scalar @$stack > 0) {
38 my $top = $stack->[scalar @$stack - 1];
40 if ($data->{'Name'} eq $top->[0]) {
45 # within a replacement; just ignore everything
46 return if (!defined($obj));
48 # within a cloning; slurp it up
49 if (ref($obj) eq 'XML::TemplateSAX::Buffer') {
50 $obj->start_element($data);
54 # substitution: see if this element matches anything. if so,
55 # descend down into the tree.
56 if (ref($obj) eq 'HASH') {
57 # first of all, see if we have an attribute match.
58 for my $key (keys %$obj) {
59 next unless ($key =~ /^(#?)(.*)\/(.*)$/);
60 my ($idmarker, $name, $attr) = ($1, $2, $3);
62 if (($idmarker eq '#' && $id eq $name) ||
63 ($idmarker ne '#' && $data->{'LocalName'} eq $name)) {
64 $data->{'Attributes'}->{$attr} = {
75 for my $key (keys %$obj) {
76 if ($key =~ /^#(.*)$/) {
77 if (defined($id) && $id eq $1) {
78 $match = $obj->{$key};
82 if ($data->{'LocalName'} eq $key) {
83 $match = $obj->{$key};
89 if (defined($match)) {
90 $self->SUPER::start_element($data);
92 push @{$self->{'stack'}}, [ $data->{'Name'}, 0, $obj ];
95 # This is sort of ugly. We special-case replacement by outputting
96 # the string immediately, and then just ignoring the rest of the
97 # events until we get to the right end tag. It's not 100% technically
98 # correct for the case where you replace an entire document by a
99 # string, but that's nonsensical anyway.
102 $self->SUPER::characters({ Data => $match });
103 $self->{'obj'} = undef;
108 # Sort of the same, for cloning. Cloning works by gobbling up all the all the
109 # input until the end element, and put it into a buffer. when we get to the end
110 # element, spew it all out again as many times as we need, onto ourselves so we
111 # get filtering etc. right.
113 # We let the buffer object keep the actual array, so we can fetch it out later.
115 if (ref($match) eq 'ARRAY') {
116 $self->{'obj'} = XML::TemplateSAX::Buffer->new($match);
121 # If someone tries to insert a full tree, do it, just like the character
124 if (ref($match) eq 'XML::TemplateSAX::Buffer') {
125 $match->replay($self);
126 $self->{'obj'} = undef;
129 $self->{'obj'} = $match;
135 $self->SUPER::start_element($data);
139 my ($self, $data) = @_;
141 return if (!defined($self->{'obj'}));
143 if (ref($self->{'obj'}) eq 'XML::TemplateSAX::Buffer') {
144 $self->{'obj'}->characters($data);
148 $self->SUPER::characters($data);
152 my ($self, $data) = @_;
154 return if (!defined($self->{'obj'}));
156 if (ref($self->{'obj'}) eq 'XML::TemplateSAX::Buffer') {
157 $self->{'obj'}->comment($data);
161 $self->SUPER::comment($data);
164 sub processing_instruction {
165 my ($self, $data) = @_;
167 return if (!defined($self->{'obj'}));
169 if (ref($self->{'obj'}) eq 'XML::TemplateSAX::Buffer') {
170 $self->{'obj'}->processing_instruction($data);
174 $self->SUPER::processing_instruction($data);
178 my ($self, $data) = @_;
180 my $stack = $self->{'stack'};
181 if (scalar @$stack > 0) {
182 my $top = $stack->[scalar @$stack - 1];
184 if ($data->{'Name'} eq $top->[0]) {
185 if ($top->[1] == 0) {
186 my $obj = $self->{'obj'};
188 # did we just finish a clone operation?
189 if (ref($obj) eq 'XML::TemplateSAX::Buffer') {
190 for my $instance (@{$obj->{'ptr'}}) {
191 $self->{'obj'} = $instance;
196 $self->SUPER::end_element($data);
197 $self->{'obj'} = $top->[2];
206 return if (!defined($self->{'obj'}));
208 if (ref($self->{'obj'}) eq 'XML::TemplateSAX::Buffer') {
209 $self->{'obj'}->end_element($data);
213 $self->SUPER::end_element($data);