File Coverage

blib/lib/XML/SAX/Writer/XML.pm
Criterion Covered Total %
statement 63 221 28.5
branch 11 64 17.1
condition 0 11 0.0
subroutine 9 29 31.0
pod 0 24 0.0
total 83 349 23.7


line stmt bran cond sub pod time code
1             package XML::SAX::Writer::XML;
2             $XML::SAX::Writer::XML::VERSION = '0.57';
3 3     3   37 use strict;
  3         6  
  3         86  
4 3     3   15 use warnings;
  3         5  
  3         70  
5 3     3   1140 use XML::NamespaceSupport qw();
  3         5259  
  3         5008  
6             @XML::SAX::Writer::XML::ISA = qw(XML::SAX::Writer);
7              
8             # ABSTRACT: XML::SAX::Writer's SAX Handler
9              
10             ###
11             # Robin Berjon
12             ###
13              
14              
15             #-------------------------------------------------------------------#
16             # start_document
17             #-------------------------------------------------------------------#
18             sub start_document {
19 14     14 0 8251 my $self = shift;
20              
21 14         66 $self->setConverter;
22 14         58 $self->setEscaperRegex;
23 14         63 $self->setAttributeEscaperRegex;
24 14         58 $self->setCommentEscaperRegex;
25              
26 14         33 $self->{NSDecl} = [];
27 14         80 $self->{NSHelper} = XML::NamespaceSupport->new({ xmlns => 1, fatal_errors => 0 });
28 14         306 $self->{NSHelper}->pushContext;
29              
30 14         195 $self->setConsumer;
31             }
32             #-------------------------------------------------------------------#
33              
34             #-------------------------------------------------------------------#
35             # end_document
36             #-------------------------------------------------------------------#
37             sub end_document {
38 5     5 0 91 my $self = shift;
39             # we may need to do a little more here
40 5         17 $self->{NSHelper}->popContext;
41             return $self->{Consumer}->finalize
42 5 50       78 if $self->{Consumer}->can( 'finalize' );
43             }
44             #-------------------------------------------------------------------#
45              
46             #-------------------------------------------------------------------#
47             # start_element
48             #-------------------------------------------------------------------#
49             sub start_element {
50 5     5 0 121 my $self = shift;
51 5         7 my $data = shift;
52 5         19 $self->_output_element;
53 5         10 my $attr = $data->{Attributes};
54              
55             # fix the namespaces and prefixes of what we're receiving, in case
56             # something is wrong
57 5 50       19 if ($data->{NamespaceURI}) {
    50          
58 0   0     0 my $uri = $self->{NSHelper}->getURI($data->{Prefix}) || '';
59 0 0       0 if ($uri ne $data->{NamespaceURI}) { # ns has precedence
60 0         0 $data->{Prefix} = $self->{NSHelper}->getPrefix($data->{NamespaceURI}); # random, but correct
61 0 0       0 $data->{Name} = $data->{Prefix} ? "$data->{Prefix}:$data->{LocalName}" : "$data->{LocalName}";
62             }
63             }
64             elsif ($data->{Prefix}) { # we can't have a prefix and no NS
65 0         0 $data->{Name} = $data->{LocalName};
66 0         0 $data->{Prefix} = '';
67             }
68              
69             # create a hash containing the attributes so that we can ensure there is
70             # no duplication. Also, we check that ns are properly declared, that the
71             # Name is good, etc...
72 5         10 my %attr_hash;
73 5         16 for my $at (values %$attr) {
74 1 50       6 next unless length $at->{Name}; # people have trouble with autovivification
75 1 50       6 if ($at->{NamespaceURI}) {
    50          
76 0         0 my $uri = $self->{NSHelper}->getURI($at->{Prefix});
77 0 0       0 warn "Well formed error: prefix '$at->{Prefix}' is not bound to any URI" unless defined $uri;
78 0 0 0     0 if (defined $uri and $uri ne $at->{NamespaceURI}) { # ns has precedence
79 0         0 $at->{Prefix} = $self->{NSHelper}->getPrefix($at->{NamespaceURI}); # random, but correct
80 0 0       0 $at->{Name} = $at->{Prefix} ? "$at->{Prefix}:$at->{LocalName}" : "$at->{LocalName}";
81             }
82             }
83             elsif ($at->{Prefix}) { # we can't have a prefix and no NS
84 0         0 $at->{Name} = $at->{LocalName};
85 0         0 $at->{Prefix} = '';
86             }
87 1         3 $attr_hash{$at->{Name}} = $at->{Value};
88             }
89              
90 5         10 for my $nd (@{$self->{NSDecl}}) {
  5         15  
91 0 0       0 if ($nd->{Prefix}) {
92 0         0 $attr_hash{'xmlns:' . $nd->{Prefix}} = $nd->{NamespaceURI};
93             }
94             else {
95 0         0 $attr_hash{'xmlns'} = $nd->{NamespaceURI};
96             }
97             }
98 5         15 $self->{NSDecl} = [];
99              
100             # build a string from what we have, and buffer it
101 5         14 my $el = '<' . $data->{Name};
102 5         14 for my $k (keys %attr_hash) {
103 1         11 $el .= ' ' . $k . qq[=$self->{QuoteCharacter}] . $self->escapeAttribute($attr_hash{$k}) . qq[$self->{QuoteCharacter}];
104             }
105              
106 5         13 $self->{BufferElement} = $el;
107 5         18 $self->{NSHelper}->pushContext;
108             }
109             #-------------------------------------------------------------------#
110              
111             #-------------------------------------------------------------------#
112             # end_element
113             #-------------------------------------------------------------------#
114             sub end_element {
115 5     5 0 141 my $self = shift;
116 5         9 my $data = shift;
117              
118 5         10 my $el;
119 5 100       14 if ($self->{BufferElement}) {
120 3         8 $el = $self->{BufferElement} . ' />';
121             }
122             else {
123 2         6 $el = '{Name} . '>';
124             }
125 5         27 $el = $self->safeConvert($el);
126 5         22 $self->{Consumer}->output($el);
127 5         27 $self->{NSHelper}->popContext;
128 5         60 $self->{BufferElement} = '';
129             }
130             #-------------------------------------------------------------------#
131              
132             #-------------------------------------------------------------------#
133             # characters
134             #-------------------------------------------------------------------#
135             sub characters {
136 2     2 0 37 my $self = shift;
137 2         5 my $data = shift;
138 2         6 $self->_output_element;
139              
140 2         4 my $char = $data->{Data};
141 2 50       6 if ($self->{InCDATA}) {
142             # we must scan for ]]> in the CDATA and escape it if it
143             # is present by close--opening
144             # we need to have buffer text in front of this...
145 0         0 $char = join ']]>]]<', $char;
146             }
147             else {
148 2         9 $char = $self->escape($char);
149             }
150 2         5 $char = $self->safeConvert($char);
151 2         7 $self->{Consumer}->output($char);
152             }
153             #-------------------------------------------------------------------#
154              
155             #-------------------------------------------------------------------#
156             # start_prefix_mapping
157             #-------------------------------------------------------------------#
158             sub start_prefix_mapping {
159 0     0 0 0 my $self = shift;
160 0         0 my $data = shift;
161              
162 0         0 push @{$self->{NSDecl}}, $data;
  0         0  
163 0         0 $self->{NSHelper}->declarePrefix($data->{Prefix}, $data->{NamespaceURI});
164             }
165             #-------------------------------------------------------------------#
166              
167             #-------------------------------------------------------------------#
168             # end_prefix_mapping
169             #-------------------------------------------------------------------#
170       0 0   sub end_prefix_mapping {}
171             #-------------------------------------------------------------------#
172              
173             #-------------------------------------------------------------------#
174             # processing_instruction
175             #-------------------------------------------------------------------#
176             sub processing_instruction {
177 0     0 0 0 my $self = shift;
178 0         0 my $data = shift;
179 0         0 $self->_output_element;
180 0         0 $self->_output_dtd;
181              
182 0         0 my $pi = "{Target} $data->{Data}?>";
183 0         0 $pi = $self->safeConvert($pi);
184 0         0 $self->{Consumer}->output($pi);
185             }
186             #-------------------------------------------------------------------#
187              
188             #-------------------------------------------------------------------#
189             # ignorable_whitespace
190             #-------------------------------------------------------------------#
191             sub ignorable_whitespace {
192 0     0 0 0 my $self = shift;
193 0         0 my $data = shift;
194 0         0 $self->_output_element;
195              
196 0         0 my $char = $data->{Data};
197 0         0 $char = $self->escape($char);
198 0         0 $char = $self->safeConvert($char);
199 0         0 $self->{Consumer}->output($char);
200             }
201             #-------------------------------------------------------------------#
202              
203             #-------------------------------------------------------------------#
204             # skipped_entity
205             #-------------------------------------------------------------------#
206             sub skipped_entity {
207 0     0 0 0 my $self = shift;
208 0         0 my $data = shift;
209 0         0 $self->_output_element;
210 0         0 $self->_output_dtd;
211              
212 0         0 my $ent;
213 0 0       0 if ($data->{Name} =~ m/^%/) {
    0          
214 0         0 $ent = $data->{Name} . ';';
215              
216             } elsif ($data->{Name} eq '[dtd]') {
217             # ignoring
218              
219             } else {
220 0         0 $ent = '&' . $data->{Name} . ';';
221             }
222              
223 0         0 $ent = $self->safeConvert($ent);
224 0         0 $self->{Consumer}->output($ent);
225              
226             }
227             #-------------------------------------------------------------------#
228              
229             #-------------------------------------------------------------------#
230             # notation_decl
231             #-------------------------------------------------------------------#
232             sub notation_decl {
233 0     0 0 0 my $self = shift;
234 0         0 my $data = shift;
235 0         0 $self->_output_dtd;
236              
237             # I think that param entities are normalized before this
238 0         0 my $not = " {Name};
239 0 0 0     0 if ($data->{PublicId} and $data->{SystemId}) {
    0          
240 0         0 $not .= ' PUBLIC \'' . $self->escape($data->{PublicId}) . '\' \'' . $self->escape($data->{SystemId}) . '\'';
241             }
242             elsif ($data->{PublicId}) {
243 0         0 $not .= ' PUBLIC \'' . $self->escape($data->{PublicId}) . '\'';
244             }
245             else {
246 0         0 $not .= ' SYSTEM \'' . $self->escape($data->{SystemId}) . '\'';
247             }
248 0         0 $not .= " >\n";
249              
250 0         0 $not = $self->safeConvert($not);
251 0         0 $self->{Consumer}->output($not);
252             }
253             #-------------------------------------------------------------------#
254              
255             #-------------------------------------------------------------------#
256             # unparsed_entity_decl
257             #-------------------------------------------------------------------#
258             sub unparsed_entity_decl {
259 0     0 0 0 my $self = shift;
260 0         0 my $data = shift;
261 0         0 $self->_output_dtd;
262              
263             # I think that param entities are normalized before this
264 0         0 my $ent = " {Name};
265 0 0       0 if ($data->{PublicId}) {
266 0         0 $ent .= ' PUBLIC \'' . $self->escape($data->{PublicId}) . '\' \'' . $self->escape($data->{SystemId}) . '\'';
267             }
268             else {
269 0         0 $ent .= ' SYSTEM \'' . $self->escape($data->{SystemId}) . '\'';
270             }
271 0         0 $ent .= " NDATA $data->{Notation} >\n";
272              
273 0         0 $ent = $self->safeConvert($ent);
274 0         0 $self->{Consumer}->output($ent);
275             }
276             #-------------------------------------------------------------------#
277              
278             #-------------------------------------------------------------------#
279             # element_decl
280             #-------------------------------------------------------------------#
281             sub element_decl {
282 0     0 0 0 my $self = shift;
283 0         0 my $data = shift;
284 0         0 $self->_output_dtd;
285              
286             # I think that param entities are normalized before this
287 0         0 my $eld = " {Name} . ' ' . $data->{Model} . " >\n";
288              
289 0         0 $eld = $self->safeConvert($eld);
290 0         0 $self->{Consumer}->output($eld);
291             }
292             #-------------------------------------------------------------------#
293              
294             #-------------------------------------------------------------------#
295             # attribute_decl
296             #-------------------------------------------------------------------#
297             sub attribute_decl {
298 0     0 0 0 my $self = shift;
299 0         0 my $data = shift;
300 0         0 $self->_output_dtd;
301              
302             # to be backward compatible with Perl SAX 2.0
303             $data->{Mode} = $data->{ValueDefault}
304 0 0 0     0 if not(exists $data->{Mode}) and exists $data->{ValueDefault};
305              
306             # I think that param entities are normalized before this
307 0         0 my $atd = " {eName} . ' ' . $data->{aName} . ' ';
308 0         0 $atd .= $data->{Type} . ' ' . $data->{Mode} . ' ';
309 0 0       0 $atd .= $data->{Value} . ' ' if $data->{Value};
310 0         0 $atd .= " >\n";
311              
312 0         0 $atd = $self->safeConvert($atd);
313 0         0 $self->{Consumer}->output($atd);
314             }
315             #-------------------------------------------------------------------#
316              
317             #-------------------------------------------------------------------#
318             # internal_entity_decl
319             #-------------------------------------------------------------------#
320             sub internal_entity_decl {
321 0     0 0 0 my $self = shift;
322 0         0 my $data = shift;
323 0         0 $self->_output_dtd;
324              
325             # I think that param entities are normalized before this
326 0         0 my $ent = " {Name} . ' \'' . $self->escape($data->{Value}) . "' >\n";
327 0         0 $ent = $self->safeConvert($ent);
328 0         0 $self->{Consumer}->output($ent);
329             }
330             #-------------------------------------------------------------------#
331              
332             #-------------------------------------------------------------------#
333             # external_entity_decl
334             #-------------------------------------------------------------------#
335             sub external_entity_decl {
336 0     0 0 0 my $self = shift;
337 0         0 my $data = shift;
338 0         0 $self->_output_dtd;
339              
340             # I think that param entities are normalized before this
341 0         0 my $ent = " {Name};
342 0 0       0 if ($data->{PublicId}) {
343 0         0 $ent .= ' PUBLIC \'' . $self->escape($data->{PublicId}) . '\' \'' . $self->escape($data->{SystemId}) . '\'';
344             }
345             else {
346 0         0 $ent .= ' SYSTEM \'' . $self->escape($data->{SystemId}) . '\'';
347             }
348 0         0 $ent .= " >\n";
349              
350 0         0 $ent = $self->safeConvert($ent);
351 0         0 $self->{Consumer}->output($ent);
352             }
353             #-------------------------------------------------------------------#
354              
355             #-------------------------------------------------------------------#
356             # comment
357             #-------------------------------------------------------------------#
358             sub comment {
359 0     0 0 0 my $self = shift;
360 0         0 my $data = shift;
361 0         0 $self->_output_element;
362 0         0 $self->_output_dtd;
363              
364 0         0 my $cmt = '';
365 0         0 $cmt = $self->safeConvert($cmt);
366 0         0 $self->{Consumer}->output($cmt);
367             }
368             #-------------------------------------------------------------------#
369              
370             #-------------------------------------------------------------------#
371             # start_dtd
372             #-------------------------------------------------------------------#
373             sub start_dtd {
374 0     0 0 0 my $self = shift;
375 0         0 my $data = shift;
376              
377 0         0 my $dtd = '{Name};
378 0 0       0 if ($data->{PublicId}) {
    0          
379 0         0 $dtd .= ' PUBLIC \'' . $self->escape($data->{PublicId}) . '\' \'' . $self->escape($data->{SystemId}) . '\'';
380             }
381             elsif ($data->{SystemId}) {
382 0         0 $dtd .= ' SYSTEM \'' . $self->escape($data->{SystemId}) . '\'';
383             }
384              
385 0         0 $self->{BufferDTD} = $dtd;
386             }
387             #-------------------------------------------------------------------#
388              
389             #-------------------------------------------------------------------#
390             # end_dtd
391             #-------------------------------------------------------------------#
392             sub end_dtd {
393 0     0 0 0 my $self = shift;
394 0         0 my $data = shift;
395              
396 0         0 my $dtd;
397 0 0       0 if ($self->{BufferDTD}) {
398 0         0 $dtd = $self->{BufferDTD} . ' >';
399             }
400             else {
401 0         0 $dtd = ' ]>';
402             }
403 0         0 $dtd = $self->safeConvert($dtd);
404 0         0 $self->{Consumer}->output($dtd);
405 0         0 $self->{BufferDTD} = '';
406             }
407             #-------------------------------------------------------------------#
408              
409             #-------------------------------------------------------------------#
410             # start_cdata
411             #-------------------------------------------------------------------#
412             sub start_cdata {
413 0     0 0 0 my $self = shift;
414 0         0 $self->_output_element;
415              
416 0         0 $self->{InCDATA} = 1;
417 0         0 my $cds = $self->{Encoder}->convert('
418 0         0 $self->{Consumer}->output($cds);
419             }
420             #-------------------------------------------------------------------#
421              
422             #-------------------------------------------------------------------#
423             # end_cdata
424             #-------------------------------------------------------------------#
425             sub end_cdata {
426 0     0 0 0 my $self = shift;
427              
428 0         0 $self->{InCDATA} = 0;
429 0         0 my $cds = $self->{Encoder}->convert(']]>');
430 0         0 $self->{Consumer}->output($cds);
431             }
432             #-------------------------------------------------------------------#
433              
434             #-------------------------------------------------------------------#
435             # start_entity
436             #-------------------------------------------------------------------#
437             sub start_entity {
438 0     0 0 0 my $self = shift;
439 0         0 my $data = shift;
440 0         0 $self->_output_element;
441 0         0 $self->_output_dtd;
442              
443 0         0 my $ent;
444 0 0       0 if ($data->{Name} eq '[dtd]') {
    0          
445             # we ignore the fact that we're dealing with an external
446             # DTD entity here, and probably shouldn't write the DTD
447             # events unless explicitly told to
448             # this will probably change
449             }
450             elsif ($data->{Name} =~ m/^%/) {
451 0         0 $ent = $data->{Name} . ';';
452             }
453             else {
454 0         0 $ent = '&' . $data->{Name} . ';';
455             }
456              
457 0         0 $ent = $self->safeConvert($ent);
458 0         0 $self->{Consumer}->output($ent);
459             }
460             #-------------------------------------------------------------------#
461              
462             #-------------------------------------------------------------------#
463             # end_entity
464             #-------------------------------------------------------------------#
465       0 0   sub end_entity {
466             # depending on what is done above, we might need to do sth here
467             }
468             #-------------------------------------------------------------------#
469              
470              
471             ### SAX1 stuff ######################################################
472              
473             #-------------------------------------------------------------------#
474             # xml_decl
475             #-------------------------------------------------------------------#
476             sub xml_decl {
477 0     0 0 0 my $self = shift;
478 0         0 my $data = shift;
479              
480             # version info is compulsory, contrary to what some seem to think
481             # also, there's order in the pseudo-attr
482 0         0 my $xd = '';
483 0 0       0 if ($data->{Version}) {
484 0         0 $xd .= "{Version}\"";
485 0 0       0 if ($data->{Encoding}) {
486 0         0 $xd .= " encoding=\"$data->{Encoding}\"";
487             }
488 0 0       0 if ($data->{Standalone}) {
489 0         0 $xd .= " standalone=\"$data->{Standalone}\"";
490             }
491 0         0 $xd .= "?>\n";
492             }
493              
494             #$xd = $self->{Encoder}->convert($xd); # this may blow up
495 0         0 $self->{Consumer}->output($xd);
496             }
497             #-------------------------------------------------------------------#
498              
499              
500             #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
501             #`,`, Helpers `,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
502             #```````````````````````````````````````````````````````````````````#
503              
504             #-------------------------------------------------------------------#
505             # _output_element
506             #-------------------------------------------------------------------#
507             sub _output_element {
508 7     7   15 my $self = shift;
509              
510 7 100       22 if ($self->{BufferElement}) {
511 2         6 my $el = $self->{BufferElement} . '>';
512 2         13 $el = $self->safeConvert($el);
513 2         8 $self->{Consumer}->output($el);
514 2         4 $self->{BufferElement} = '';
515             }
516             }
517             #-------------------------------------------------------------------#
518              
519             #-------------------------------------------------------------------#
520             # _output_dtd
521             #-------------------------------------------------------------------#
522             sub _output_dtd {
523 0     0     my $self = shift;
524              
525 0 0         if ($self->{BufferDTD}) {
526 0           my $dtd = $self->{BufferDTD} . " [\n";
527 0           $dtd = $self->safeConvert($dtd);
528 0           $self->{Consumer}->output($dtd);
529 0           $self->{BufferDTD} = '';
530             }
531             }
532             #-------------------------------------------------------------------#
533              
534             1;
535             #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
536             #`,`, Documentation `,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
537             #```````````````````````````````````````````````````````````````````#
538              
539             __END__