File Coverage

blib/lib/XML/LibXML/SAX/Parser.pm
Criterion Covered Total %
statement 88 109 80.7
branch 28 52 53.8
condition 9 20 45.0
subroutine 12 21 57.1
pod 0 3 0.0
total 137 205 66.8


line stmt bran cond sub pod time code
1             # $Id$
2             #
3             # This is free software, you may use it and distribute it under the same terms as
4             # Perl itself.
5             #
6             # Copyright 2001-2003 AxKit.com Ltd., 2002-2006 Christian Glahn, 2006-2009 Petr Pajas
7             #
8             #
9              
10             package XML::LibXML::SAX::Parser;
11              
12 1     1   416 use strict;
  1         2  
  1         24  
13 1     1   4 use warnings;
  1         1  
  1         23  
14 1     1   4 use vars qw($VERSION @ISA);
  1         2  
  1         34  
15              
16 1     1   12 use XML::LibXML;
  1         3  
  1         5  
17 1     1   413 use XML::LibXML::Common qw(:libxml);
  1         2  
  1         256  
18 1     1   6 use XML::SAX::Base;
  1         1  
  1         29  
19 1     1   360 use XML::SAX::DocumentLocator;
  1         500  
  1         1047  
20              
21             $VERSION = "2.0208"; # VERSION TEMPLATE: DO NOT CHANGE
22             @ISA = ('XML::SAX::Base');
23              
24             sub CLONE_SKIP {
25 0 0   0   0 return $XML::LibXML::__threads_shared ? 0 : 1;
26             }
27              
28             sub _parse_characterstream {
29 0     0   0 my ($self, $fh, $options) = @_;
30 0         0 die "parsing a characterstream is not supported at this time";
31             }
32              
33             sub _parse_bytestream {
34 0     0   0 my ($self, $fh, $options) = @_;
35 0         0 my $parser = XML::LibXML->new();
36 0 0       0 my $doc = exists($options->{Source}{SystemId}) ? $parser->parse_fh($fh, $options->{Source}{SystemId}) : $parser->parse_fh($fh);
37 0         0 $self->generate($doc);
38             }
39              
40             sub _parse_string {
41 3     3   3377 my ($self, $str, $options) = @_;
42 3         11 my $parser = XML::LibXML->new();
43 3 50       12 my $doc = exists($options->{Source}{SystemId}) ? $parser->parse_string($str, $options->{Source}{SystemId}) : $parser->parse_string($str);
44 2         6 $self->generate($doc);
45             }
46              
47             sub _parse_systemid {
48 2     2   2742 my ($self, $sysid, $options) = @_;
49 2         7 my $parser = XML::LibXML->new();
50 2         7 my $doc = $parser->parse_file($sysid);
51 2         4 $self->generate($doc);
52             }
53              
54             sub generate {
55 6     6 0 891 my $self = shift;
56 6         11 my ($node) = @_;
57              
58 6         53 my $doc = $node->ownerDocument();
59             {
60             # precompute some DocumentLocator values
61 6         10 my %locator = (
  6         20  
62             PublicId => undef,
63             SystemId => undef,
64             Encoding => undef,
65             XMLVersion => undef,
66             );
67 6 50       36 my $dtd = defined $doc ? $doc->externalSubset() : undef;
68 6 50       14 if (defined $dtd) {
69 0         0 $locator{PublicId} = $dtd->publicId();
70 0         0 $locator{SystemId} = $dtd->systemId();
71             }
72 6 50       9 if (defined $doc) {
73 6         26 $locator{Encoding} = $doc->encoding();
74 6         15 $locator{XMLVersion} = $doc->version();
75             }
76             $self->set_document_locator(
77             XML::SAX::DocumentLocator->new(
78 0     0   0 sub { $locator{PublicId} },
79 0     0   0 sub { $locator{SystemId} },
80 0 0   0   0 sub { defined($self->{current_node}) ? $self->{current_node}->line_number() : undef },
81 0     0   0 sub { 1 },
82 0     0   0 sub { $locator{Encoding} },
83 0     0   0 sub { $locator{XMLVersion} },
84 6         56 ),
85             );
86             }
87              
88 6 50 33     225 if ( $node->nodeType() == XML_DOCUMENT_NODE
89             || $node->nodeType == XML_HTML_DOCUMENT_NODE ) {
90 6         22 $self->start_document({});
91 6         189 $self->xml_decl({Version => $node->getVersion, Encoding => $node->getEncoding});
92 6         111 $self->process_node($node);
93 6         63 $self->end_document({});
94             }
95             }
96              
97             sub process_node {
98 104     104 0 143 my ($self, $node) = @_;
99              
100 104         174 local $self->{current_node} = $node;
101              
102 104         203 my $node_type = $node->nodeType();
103 104 50 66     290 if ($node_type == XML_COMMENT_NODE) {
    100 33        
    100 33        
    50 0        
    50          
    0          
    0          
    0          
    0          
104 0         0 $self->comment( { Data => $node->getData } );
105             }
106             elsif ($node_type == XML_TEXT_NODE
107             || $node_type == XML_CDATA_SECTION_NODE) {
108             # warn($node->getData . "\n");
109 62         266 $self->characters( { Data => $node->nodeValue } );
110             }
111             elsif ($node_type == XML_ELEMENT_NODE) {
112             # warn("<" . $node->getName . ">\n");
113 36         68 $self->process_element($node);
114             # warn("getName . ">\n");
115             }
116             elsif ($node_type == XML_ENTITY_REF_NODE) {
117 0         0 foreach my $kid ($node->childNodes) {
118             # warn("child of entity ref: " . $kid->getType() . " called: " . $kid->getName . "\n");
119 0         0 $self->process_node($kid);
120             }
121             }
122             elsif ($node_type == XML_DOCUMENT_NODE
123             || $node_type == XML_HTML_DOCUMENT_NODE
124             || $node_type == XML_DOCUMENT_FRAG_NODE) {
125             # sometimes it is just useful to generate SAX events from
126             # a document fragment (very good with filters).
127 6         18 foreach my $kid ($node->childNodes) {
128 6         11 $self->process_node($kid);
129             }
130             }
131             elsif ($node_type == XML_PI_NODE) {
132 0         0 $self->processing_instruction( { Target => $node->getName, Data => $node->getData } );
133             }
134             elsif ($node_type == XML_COMMENT_NODE) {
135 0         0 $self->comment( { Data => $node->getData } );
136             }
137             elsif ( $node_type == XML_XINCLUDE_START
138             || $node_type == XML_XINCLUDE_END ) {
139             # ignore!
140             # i may want to handle this one day, dunno yet
141             }
142             elsif ($node_type == XML_DTD_NODE ) {
143             # ignore!
144             # i will support DTDs, but had no time yet.
145             }
146             else {
147             # warn("unsupported node type: $node_type");
148             }
149              
150             }
151              
152             sub process_element {
153 36     36 0 48 my ($self, $element) = @_;
154              
155 36         46 my $attribs = {};
156 36         93 my @ns_maps = $element->getNamespaces;
157              
158 36         78 foreach my $ns (@ns_maps) {
159 4 100       106 $self->start_prefix_mapping(
160             {
161             NamespaceURI => $ns->href,
162             Prefix => ( defined $ns->localname ? $ns->localname : ''),
163             }
164             );
165             }
166              
167 36         145 foreach my $attr ($element->attributes) {
168 17         22 my $key;
169             # warn("Attr: $attr -> ", $attr->getName, " = ", $attr->getData, "\n");
170             # this isa dump thing...
171 17 100       57 if ($attr->isa('XML::LibXML::Namespace')) {
172             # TODO This needs fixing modulo agreeing on what
173             # is the right thing to do here.
174 4 50       9 unless ( defined $attr->name ) {
175             ## It's an atter like "xmlns='foo'"
176 0         0 $attribs->{"{}xmlns"} =
177             {
178             Name => "xmlns",
179             LocalName => "xmlns",
180             Prefix => "",
181             Value => $attr->href,
182             NamespaceURI => "",
183             };
184             }
185             else {
186 4         8 my $prefix = "xmlns";
187 4         9 my $localname = $attr->localname;
188 4         4 my $key = "{http://www.w3.org/2000/xmlns/}";
189 4         5 my $name = "xmlns";
190              
191 4 100       8 if ( defined $localname ) {
192 2         4 $key .= $localname;
193 2         4 $name.= ":".$localname;
194             }
195              
196 4         22 $attribs->{$key} =
197             {
198             Name => $name,
199             Value => $attr->href,
200             NamespaceURI => "http://www.w3.org/2000/xmlns/",
201             Prefix => $prefix,
202             LocalName => $localname,
203             };
204             }
205             }
206             else {
207 13         30 my $ns = $attr->namespaceURI;
208              
209 13 100       26 $ns = '' unless defined $ns;
210 13         41 $key = "{$ns}".$attr->localname;
211             ## Not sure why, but $attr->name is coming through stripped
212             ## of its prefix, so we need to hand-assemble a real name.
213 13         30 my $name = $attr->name;
214 13 50       23 $name = "" unless defined $name;
215              
216 13         26 my $prefix = $attr->prefix;
217 13 100       19 $prefix = "" unless defined $prefix;
218 13 100 66     50 $name = "$prefix:$name"
219             if index( $name, ":" ) < 0 && length $prefix;
220              
221 13         90 $attribs->{$key} =
222             {
223             Name => $name,
224             Value => $attr->value,
225             NamespaceURI => $ns,
226             Prefix => $prefix,
227             LocalName => $attr->localname,
228             };
229             }
230             # use Data::Dumper;
231             # warn("Attr made: ", Dumper($attribs->{$key}), "\n");
232             }
233              
234 36   100     286 my $node = {
235             Name => $element->nodeName,
236             Attributes => $attribs,
237             NamespaceURI => $element->namespaceURI,
238             Prefix => $element->prefix || "",
239             LocalName => $element->localname,
240             };
241              
242 36         101 $self->start_element($node);
243              
244 36         884 foreach my $child ($element->childNodes) {
245 92         385 $self->process_node($child);
246             }
247              
248 36         848 my $end_node = { %$node };
249              
250 36         68 delete $end_node->{Attributes};
251              
252 36         90 $self->end_element($end_node);
253              
254 36         326 foreach my $ns (@ns_maps) {
255 4 100       57 $self->end_prefix_mapping(
256             {
257             NamespaceURI => $ns->href,
258             Prefix => ( defined $ns->localname ? $ns->localname : ''),
259             }
260             );
261             }
262             }
263              
264             1;
265              
266             __END__