File Coverage

blib/lib/XML/SAX/Expat.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1            
2             ###
3             # XML::SAX::Expat - SAX2 Driver for Expat (XML::Parser)
4             # Originally by Robin Berjon
5             ###
6            
7             package XML::SAX::Expat;
8 1     1   2036 use strict;
  1         2  
  1         49  
9 1     1   6 use base qw(XML::SAX::Base);
  1         2  
  1         2006  
10 1     1   28432 use XML::NamespaceSupport qw();
  1         2817  
  1         22  
11 1     1   1615 use XML::Parser qw();
  0            
  0            
12            
13             use vars qw($VERSION);
14             $VERSION = '0.51';
15            
16            
17             #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
18             #`,`, Variations on parse `,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
19             #```````````````````````````````````````````````````````````````````#
20            
21             #-------------------------------------------------------------------#
22             # CharacterStream
23             #-------------------------------------------------------------------#
24             sub _parse_characterstream {
25             my $p = shift;
26             my $xml = shift;
27             my $opt = shift;
28            
29             my $expat = $p->_create_parser($opt);
30             my $result = $expat->parse($xml);
31             $p->_cleanup;
32             return $result;
33             }
34             #-------------------------------------------------------------------#
35            
36             #-------------------------------------------------------------------#
37             # ByteStream
38             #-------------------------------------------------------------------#
39             sub _parse_bytestream {
40             my $p = shift;
41             my $xml = shift;
42             my $opt = shift;
43            
44             my $expat = $p->_create_parser($opt);
45             my $result = $expat->parse($xml);
46             $p->_cleanup;
47             return $result;
48             }
49             #-------------------------------------------------------------------#
50            
51             #-------------------------------------------------------------------#
52             # String
53             #-------------------------------------------------------------------#
54             sub _parse_string {
55             my $p = shift;
56             my $xml = shift;
57             my $opt = shift;
58            
59             my $expat = $p->_create_parser($opt);
60             my $result = $expat->parse($xml);
61             $p->_cleanup;
62             return $result;
63             }
64             #-------------------------------------------------------------------#
65            
66             #-------------------------------------------------------------------#
67             # SystemId
68             #-------------------------------------------------------------------#
69             sub _parse_systemid {
70             my $p = shift;
71             my $xml = shift;
72             my $opt = shift;
73            
74             my $expat = $p->_create_parser($opt);
75             my $result = $expat->parsefile($xml);
76             $p->_cleanup;
77             return $result;
78             }
79             #-------------------------------------------------------------------#
80            
81            
82             #-------------------------------------------------------------------#
83             # $p->_create_parser(\%options)
84             #-------------------------------------------------------------------#
85             sub _create_parser {
86             my $self = shift;
87             my $opt = shift;
88            
89             die "ParserReference: parser instance ($self) already parsing\n"
90             if $self->{_InParse};
91            
92             my $featUri = 'http://xml.org/sax/features/';
93             my $ppe = ($self->get_feature($featUri . 'external-general-entities') or
94             $self->get_feature($featUri . 'external-parameter-entities') ) ? 1 : 0;
95            
96             my $expat = XML::Parser->new( ParseParamEnt => $ppe );
97             $expat->{__XSE} = $self;
98             $expat->setHandlers(
99             Init => \&_handle_init,
100             Final => \&_handle_final,
101             Start => \&_handle_start,
102             End => \&_handle_end,
103             Char => \&_handle_char,
104             Comment => \&_handle_comment,
105             Proc => \&_handle_proc,
106             CdataStart => \&_handle_start_cdata,
107             CdataEnd => \&_handle_end_cdata,
108             Unparsed => \&_handle_unparsed_entity,
109             Notation => \&_handle_notation_decl,
110             #ExternEnt
111             #ExternEntFin
112             Entity => \&_handle_entity_decl,
113             Element => \&_handle_element_decl,
114             Attlist => \&_handle_attr_decl,
115             Doctype => \&_handle_start_doctype,
116             DoctypeFin => \&_handle_end_doctype,
117             XMLDecl => \&_handle_xml_decl,
118             );
119            
120             $self->{_InParse} = 1;
121             $self->{_NodeStack} = [];
122             $self->{_NSStack} = [];
123             $self->{_NSHelper} = XML::NamespaceSupport->new({xmlns => 1});
124             $self->{_started} = 0;
125            
126             return $expat;
127             }
128             #-------------------------------------------------------------------#
129            
130            
131             #-------------------------------------------------------------------#
132             # $p->_cleanup
133             #-------------------------------------------------------------------#
134             sub _cleanup {
135             my $self = shift;
136            
137             $self->{_InParse} = 0;
138             delete $self->{_NodeStack};
139             }
140             #-------------------------------------------------------------------#
141            
142            
143            
144             #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
145             #`,`, Expat Handlers ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
146             #```````````````````````````````````````````````````````````````````#
147            
148             #-------------------------------------------------------------------#
149             # _handle_init
150             #-------------------------------------------------------------------#
151             sub _handle_init {
152             #my $self = shift()->{__XSE};
153            
154             #my $document = {};
155             #push @{$self->{_NodeStack}}, $document;
156             #$self->SUPER::start_document($document);
157             }
158             #-------------------------------------------------------------------#
159            
160             #-------------------------------------------------------------------#
161             # _handle_final
162             #-------------------------------------------------------------------#
163             sub _handle_final {
164             my $self = shift()->{__XSE};
165            
166             #my $document = pop @{$self->{_NodeStack}};
167             return $self->SUPER::end_document({});
168             }
169             #-------------------------------------------------------------------#
170            
171             #-------------------------------------------------------------------#
172             # _handle_start
173             #-------------------------------------------------------------------#
174             sub _handle_start {
175             my $self = shift()->{__XSE};
176             my $e_name = shift;
177             my %attr = @_;
178            
179             # start_document data
180             $self->_handle_start_document({}) unless $self->{_started};
181            
182             # take care of namespaces
183             my $nsh = $self->{_NSHelper};
184             $nsh->push_context;
185             my @new_ns;
186             for my $k (grep !index($_, 'xmlns'), keys %attr) {
187             $k =~ m/^xmlns(:(.*))?$/;
188             my $prefix = $2 || '';
189             $nsh->declare_prefix($prefix, $attr{$k});
190             my $ns = {
191             Prefix => $prefix,
192             NamespaceURI => $attr{$k},
193             };
194             push @new_ns, $ns;
195             $self->SUPER::start_prefix_mapping($ns);
196             }
197             push @{$self->{_NSStack}}, \@new_ns;
198            
199            
200             # create the attributes
201             my %saxattr;
202             map {
203             my ($ns,$prefix,$lname) = $nsh->process_attribute_name($_);
204             $saxattr{'{' . ($ns || '') . '}' . $lname} = {
205             Name => $_,
206             LocalName => $lname || '',
207             Prefix => $prefix || '',
208             Value => $attr{$_},
209             NamespaceURI => $ns || '',
210             };
211             } keys %attr;
212            
213            
214             # now the element
215             my ($ns,$prefix,$lname) = $nsh->process_element_name($e_name);
216             my $element = {
217             Name => $e_name,
218             LocalName => $lname || '',
219             Prefix => $prefix || '',
220             NamespaceURI => $ns || '',
221             Attributes => \%saxattr,
222             };
223            
224             push @{$self->{_NodeStack}}, $element;
225             $self->SUPER::start_element($element);
226             }
227             #-------------------------------------------------------------------#
228            
229             #-------------------------------------------------------------------#
230             # _handle_end
231             #-------------------------------------------------------------------#
232             sub _handle_end {
233             my $self = shift()->{__XSE};
234            
235             my %element = %{pop @{$self->{_NodeStack}}};
236             delete $element{Attributes};
237             $self->SUPER::end_element(\%element);
238            
239             my $prev_ns = pop @{$self->{_NSStack}};
240             for my $ns (@$prev_ns) {
241             $self->SUPER::end_prefix_mapping( { %$ns } );
242             }
243             $self->{_NSHelper}->pop_context;
244             }
245             #-------------------------------------------------------------------#
246            
247             #-------------------------------------------------------------------#
248             # _handle_char
249             #-------------------------------------------------------------------#
250             sub _handle_char {
251             $_[0]->{__XSE}->_handle_start_document({}) unless $_[0]->{__XSE}->{_started};
252             $_[0]->{__XSE}->SUPER::characters({ Data => $_[1] });
253             }
254             #-------------------------------------------------------------------#
255            
256             #-------------------------------------------------------------------#
257             # _handle_comment
258             #-------------------------------------------------------------------#
259             sub _handle_comment {
260             $_[0]->{__XSE}->_handle_start_document({}) unless $_[0]->{__XSE}->{_started};
261             $_[0]->{__XSE}->SUPER::comment({ Data => $_[1] });
262             }
263             #-------------------------------------------------------------------#
264            
265             #-------------------------------------------------------------------#
266             # _handle_proc
267             #-------------------------------------------------------------------#
268             sub _handle_proc {
269             $_[0]->{__XSE}->_handle_start_document({}) unless $_[0]->{__XSE}->{_started};
270             $_[0]->{__XSE}->SUPER::processing_instruction({ Target => $_[1], Data => $_[2] });
271             }
272             #-------------------------------------------------------------------#
273            
274             #-------------------------------------------------------------------#
275             # _handle_start_cdata
276             #-------------------------------------------------------------------#
277             sub _handle_start_cdata {
278             $_[0]->{__XSE}->SUPER::start_cdata( {} );
279             }
280             #-------------------------------------------------------------------#
281            
282             #-------------------------------------------------------------------#
283             # _handle_end_cdata
284             #-------------------------------------------------------------------#
285             sub _handle_end_cdata {
286             $_[0]->{__XSE}->SUPER::end_cdata( {} );
287             }
288             #-------------------------------------------------------------------#
289            
290             #-------------------------------------------------------------------#
291             # _handle_xml_decl
292             #-------------------------------------------------------------------#
293             sub _handle_xml_decl {
294             my $self = shift()->{__XSE};
295             my $version = shift;
296             my $encoding = shift;
297             my $standalone = shift;
298            
299             if (not defined $standalone) { $standalone = ''; }
300             elsif ($standalone) { $standalone = 'yes'; }
301             else { $standalone = 'no'; }
302             my $xd = {
303             Version => $version,
304             Encoding => $encoding,
305             Standalone => $standalone,
306             };
307             #$self->SUPER::xml_decl($xd);
308             $self->_handle_start_document($xd);
309             }
310             #-------------------------------------------------------------------#
311            
312             #-------------------------------------------------------------------#
313             # _handle_notation_decl
314             #-------------------------------------------------------------------#
315             sub _handle_notation_decl {
316             my $self = shift()->{__XSE};
317             my $notation = shift;
318             shift;
319             my $system = shift;
320             my $public = shift;
321            
322             my $not = {
323             Name => $notation,
324             PublicId => $public,
325             SystemId => $system,
326             };
327             $self->SUPER::notation_decl($not);
328             }
329             #-------------------------------------------------------------------#
330            
331             #-------------------------------------------------------------------#
332             # _handle_unparsed_entity
333             #-------------------------------------------------------------------#
334             sub _handle_unparsed_entity {
335             my $self = shift()->{__XSE};
336             my $name = shift;
337             my $system = shift;
338             my $public = shift;
339             my $notation = shift;
340            
341             my $ue = {
342             Name => $name,
343             PublicId => $public,
344             SystemId => $system,
345             Notation => $notation,
346             };
347             $self->SUPER::unparsed_entity_decl($ue);
348             }
349             #-------------------------------------------------------------------#
350            
351             #-------------------------------------------------------------------#
352             # _handle_element_decl
353             #-------------------------------------------------------------------#
354             sub _handle_element_decl {
355             $_[0]->{__XSE}->SUPER::element_decl({ Name => $_[1], Model => "$_[2]" });
356             }
357             #-------------------------------------------------------------------#
358            
359            
360             #-------------------------------------------------------------------#
361             # _handle_attr_decl
362             #-------------------------------------------------------------------#
363             sub _handle_attr_decl {
364             my $self = shift()->{__XSE};
365             my $ename = shift;
366             my $aname = shift;
367             my $type = shift;
368             my $default = shift;
369             my $fixed = shift;
370            
371             my ($vd, $value);
372             if ($fixed) {
373             $vd = '#FIXED';
374             $default =~ s/^(?:"|')//; #"
375             $default =~ s/(?:"|')$//; #"
376             $value = $default;
377             }
378             else {
379             if ($default =~ m/^#/) {
380             $vd = $default;
381             $value = '';
382             }
383             else {
384             $vd = ''; # maybe there's a default ?
385             $default =~ s/^(?:"|')//; #"
386             $default =~ s/(?:"|')$//; #"
387             $value = $default;
388             }
389             }
390            
391             my $at = {
392             eName => $ename,
393             aName => $aname,
394             Type => $type,
395             ValueDefault => $vd,
396             Value => $value,
397             };
398             $self->SUPER::attribute_decl($at);
399             }
400             #-------------------------------------------------------------------#
401            
402             #-------------------------------------------------------------------#
403             # _handle_entity_decl
404             #-------------------------------------------------------------------#
405             sub _handle_entity_decl {
406             my $self = shift()->{__XSE};
407             my $name = shift;
408             my $val = shift;
409             my $sys = shift;
410             my $pub = shift;
411             my $ndata = shift;
412             my $isprm = shift;
413            
414             # deal with param ents
415             if ($isprm) {
416             $name = '%' . $name;
417             }
418            
419             # int vs ext
420             if ($val) {
421             my $ent = {
422             Name => $name,
423             Value => $val,
424             };
425             $self->SUPER::internal_entity_decl($ent);
426             }
427             else {
428             my $ent = {
429             Name => $name,
430             PublicId => $pub || '',
431             SystemId => $sys,
432             };
433             $self->SUPER::external_entity_decl($ent);
434             }
435             }
436             #-------------------------------------------------------------------#
437            
438            
439             #-------------------------------------------------------------------#
440             # _handle_start_doctype
441             #-------------------------------------------------------------------#
442             sub _handle_start_doctype {
443             my $self = shift()->{__XSE};
444             my $name = shift;
445             my $sys = shift;
446             my $pub = shift;
447            
448             $self->_handle_start_document({}) unless $self->{_started};
449            
450             my $dtd = {
451             Name => $name,
452             SystemId => $sys,
453             PublicId => $pub,
454             };
455             $self->SUPER::start_dtd($dtd);
456             }
457             #-------------------------------------------------------------------#
458            
459             #-------------------------------------------------------------------#
460             # _handle_end_doctype
461             #-------------------------------------------------------------------#
462             sub _handle_end_doctype {
463             $_[0]->{__XSE}->SUPER::end_dtd( {} );
464             }
465             #-------------------------------------------------------------------#
466            
467            
468             #-------------------------------------------------------------------#
469             # _handle_start_document
470             #-------------------------------------------------------------------#
471             sub _handle_start_document {
472             $_[0]->SUPER::start_document($_[1]);
473             $_[0]->{_started} = 1;
474             }
475             #-------------------------------------------------------------------#
476            
477            
478             #-------------------------------------------------------------------#
479             # supported_features
480             #-------------------------------------------------------------------#
481             sub supported_features {
482             return (
483             $_[0]->SUPER::supported_features,
484             'http://xml.org/sax/features/external-general-entities',
485             'http://xml.org/sax/features/external-parameter-entities',
486             );
487             }
488             #-------------------------------------------------------------------#
489            
490            
491            
492            
493            
494             #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
495             #`,`, Private Helpers `,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
496             #```````````````````````````````````````````````````````````````````#
497            
498             #-------------------------------------------------------------------#
499             # _create_node
500             #-------------------------------------------------------------------#
501             #sub _create_node {
502             # shift;
503             # # this may check for a factory later
504             # return {@_};
505             #}
506             #-------------------------------------------------------------------#
507            
508            
509             1;
510             #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
511             #`,`, Documentation `,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
512             #```````````````````````````````````````````````````````````````````#
513            
514             =pod
515            
516             =head1 NAME
517            
518             XML::SAX::Expat - SAX2 Driver for Expat (XML::Parser)
519            
520             =head1 SYNOPSIS
521            
522             use XML::SAX::Expat;
523             use XML::SAX::MyFooHandler;
524             my $h = XML::SAX::MyFooHandler->new;
525             my $p = XML::SAX::Expat->new(Handler => $h);
526             $p->parse_file('/path/to/foo.xml');
527            
528             =head1 DESCRIPTION
529            
530             This is an implementation of a SAX2 driver sitting on top of Expat
531             (XML::Parser) which Ken MacLeod posted to perl-xml and which I have
532             updated.
533            
534             It is still incomplete, though most of the basic SAX2 events should be
535             available. The SAX2 spec is currently available from L.
536            
537             A more friendly URL as well as a PODification of the spec are in the
538             works.
539            
540             =head1 METHODS
541            
542             The methods defined in this class correspond to those listed in the
543             PerlSAX2 specification, available above.
544            
545             =head1 FEATURES AND CAVEATS
546            
547             =over 2
548            
549             =item supported_features
550            
551             Returns:
552            
553             * http://xml.org/sax/features/external-general-entities
554             * http://xml.org/sax/features/external-parameter-entities
555             * [ Features supported by ancestors ]
556            
557             Turning one of the first two on also turns the other on (this maps
558             to the XML::Parser ParseParamEnts option). This may be fixed in the
559             future, so don't rely on this behaviour.
560            
561             =back
562            
563             =head1 MISSING PARTS
564            
565             XML::Parser has no listed callbacks for the following events, which
566             are therefore not presently generated (ways may be found in the
567             future):
568            
569             * ignorable_whitespace
570             * skipped_entity
571             * start_entity / end_entity
572             * resolve_entity
573            
574             Ways of signalling them are welcome. In addition to those,
575             set_document_locator is not yet called.
576            
577             =head1 TODO
578            
579             - reuse Ken's tests and add more
580            
581             =head1 AUTHOR
582            
583             Robin Berjon; stolen from Ken Macleod, ken@bitsko.slc.ut.us, and with
584             suggestions and feedback from perl-xml. Currently maintained by Bjoern
585             Hoehrmann, L.
586            
587             =head1 COPYRIGHT AND LICENSE
588            
589             Copyright (c) 2001-2008 Robin Berjon. All rights reserved. This program is
590             free software; you can redistribute it and/or modify it under the same
591             terms as Perl itself.
592            
593             =head1 SEE ALSO
594            
595             XML::Parser::PerlSAX
596            
597             =cut