File Coverage

blib/lib/XML/Struct/Writer/Stream.pm
Criterion Covered Total %
statement 78 81 96.3
branch 24 28 85.7
condition 4 5 80.0
subroutine 9 9 100.0
pod 0 6 0.0
total 115 129 89.1


line stmt bran cond sub pod time code
1             package XML::Struct::Writer::Stream;
2 7     7   52 use strict;
  7         17  
  7         206  
3 7     7   37 use Moo;
  7         17  
  7         46  
4              
5             our $VERSION = '0.27';
6              
7             has fh => (is => 'rw', default => sub { *STDOUT });
8             has pretty => (is => 'rw');
9              
10             our %ESCAPE = (
11             '&' => '&',
12             '<' => '&lt;',
13             '>' => '&gt;',
14             '"' => '&quot;',
15             );
16              
17             use constant {
18 7         6800 DOCUMENT_STARTED => 0,
19             TAG_STARTED => 1,
20             CHAR_CONTENT => 2,
21             CHILD_ELEMENT => 3,
22 7     7   3075 };
  7         15  
23              
24             sub xml_decl {
25 3     3 0 32 my ($self, $data) = @_;
26              
27 3         9 my $xml = "<?xml version=\"$data->{Version}\"";
28 3 50       12 $xml .= " encoding=\"$data->{Encoding}\"" if $data->{Encoding};
29 3 50       7 $xml .= " standalone=\"$data->{Standalone}\"" if $data->{Standalone};
30 3         5 $xml .= "?>\n";
31              
32 3         4 print {$self->fh} $xml;
  3         20  
33             }
34              
35             sub start_document {
36 12     12 0 2945 my ($self) = @_;
37 12         27 $self->{_stack} = [];
38 12         29 $self->{_status} = DOCUMENT_STARTED;
39             }
40              
41             sub start_element {
42 36     36 0 261 my ($self, $data) = @_;
43              
44 36         64 my $tag = $data->{Name};
45 36         49 my $attr = $data->{Attributes};
46 36         68 my $xml = "<$tag";
47 36   50     79 my $status = $self->{_status} // DOCUMENT_STARTED;
48              
49 36 100       93 if ($status == TAG_STARTED) {
    100          
    100          
50 15         20 print {$self->fh} '>';
  15         48  
51 15 100       39 if ($self->pretty) {
52 1         3 print {$self->fh} "\n".(' ' x (scalar @{$self->{_stack}}));
  1         4  
  1         5  
53             }
54             } elsif ($status == CHILD_ELEMENT) {
55 7 100       21 if ($self->pretty) {
56 1         2 print {$self->fh} "\n".(' ' x (scalar @{$self->{_stack}}));
  1         17  
  1         5  
57             }
58             } elsif ($status == CHAR_CONTENT) {
59 2         5 print {$self->fh} $self->{_chars};
  2         10  
60             } # else: DOCUMENT_STARTED
61              
62 36         56 push @{$self->{_stack}}, $tag;
  36         96  
63              
64 36 100 100     128 if ($attr && %$attr) {
65 6         22 foreach my $key (sort keys %$attr) {
66 6         10 my $value = $attr->{$key};
67 6         17 $value =~ s/([&<>"])/$ESCAPE{$1}/geo;
  0         0  
68 6         17 $xml .= " $key=\"$value\"";
69             }
70             }
71              
72 36         64 $self->{_status} = TAG_STARTED;
73              
74 36         51 print {$self->fh} $xml;
  36         156  
75             }
76              
77             sub end_element {
78 36     36 0 284 my ($self) = @_;
79              
80 36 50       47 my $tag = pop @{$self->{_stack}} or return;
  36         94  
81              
82 36 100       106 if ($self->{_status} == TAG_STARTED) {
    100          
83 7         11 print {$self->fh} '/>';
  7         23  
84             } elsif ($self->{_status} == CHAR_CONTENT) {
85 12         20 print {$self->fh} $self->{_chars} . "</$tag>";
  12         50  
86 12         26 $self->{_chars} = "";
87             } else { # CHILD_ELEMENT
88 17 100       52 if ($self->pretty) {
89 1         3 print {$self->fh} "\n".(' ' x (scalar @{$self->{_stack}}));
  1         14  
  1         4  
90             }
91 17         34 print {$self->fh} "</$tag>";
  17         54  
92             }
93              
94 36         128 $self->{_status} = CHILD_ELEMENT;
95             }
96              
97             sub characters {
98 16     16 0 134 my ($self, $data) = @_;
99              
100 16         30 my $xml = $data->{Data};
101 16         39 $xml =~ s/([&<>])/$ESCAPE{$1}/geo;
  0         0  
102              
103 16 100       41 if ($self->{_status} == TAG_STARTED) {
    50          
104 14         21 print {$self->fh} '>';
  14         37  
105 14         28 $self->{_status} = CHAR_CONTENT;
106 14         47 $self->{_chars} = $xml;
107             } elsif ($self->{_status} == CHILD_ELEMENT) {
108 2         4 print {$self->fh} $xml;
  2         13  
109             } else {
110 0         0 $self->{_chars} .= $xml;
111             }
112             }
113              
114             sub end_document {
115 12     12 0 87 my ($self) = @_;
116 12         25 $self->end_element while @{$self->{_stack}};
  12         33  
117 12         18 print {$self->fh} "\n";
  12         41  
118             }
119              
120              
121             1;
122             __END__
123              
124             =head1 NAME
125              
126             XML::Struct::Writer::Stream - simplified SAX handler to serialize (Micro)XML
127              
128             =head1 DESCRIPTION
129              
130             This class implements a simplfied SAX handler for stream-based serialization
131             of XML. DTDs, comments, processing instructions and similar features not part
132             of MicroXML are not supported.
133              
134             The handler is written to reproduce the serialization of libxml.
135              
136             =head1 CONFIGURATION
137              
138             =over
139              
140             =item fh
141              
142             File handle or compatible object to write to (standard output by default).
143              
144             =item pretty
145              
146             Pretty-print XML if enabled.
147              
148             =back
149              
150             =head1 SEE ALSO
151              
152             See L<XML::SAX::Writer>, L<XML::Genx::SAXWriter>, and L<XML::Handler::YAWriter>
153             for more elaborated SAX writers and L<XML::Writer> for a general XML writer,
154             not based on SAX.
155              
156             =cut