File Coverage

blib/lib/XML/SAX/Writer/XML.pm
Criterion Covered Total %
statement 55 219 25.1
branch 8 64 12.5
condition 0 11 0.0
subroutine 8 28 28.5
pod 0 24 0.0
total 71 346 20.5


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