File Coverage

blib/lib/XML/SAX/Writer/XML.pm
Criterion Covered Total %
statement 55 220 25.0
branch 8 62 12.9
condition 0 8 0.0
subroutine 8 28 28.5
pod 0 24 0.0
total 71 342 20.7


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