File Coverage

blib/lib/POE/Filter/XML.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package POE::Filter::XML;
2             {
3             $POE::Filter::XML::VERSION = '1.140700';
4             }
5              
6             #ABSTRACT: XML parsing for the POE framework
7              
8 2     2   46711 use Moose;
  0            
  0            
9             use MooseX::NonMoose;
10              
11              
12             extends 'Moose::Object','POE::Filter';
13              
14             use Carp;
15             use XML::LibXML;
16             use POE::Filter::XML::Handler;
17              
18              
19             has buffer =>
20             (
21             is => 'ro',
22             traits => [ 'Array' ],
23             isa => 'ArrayRef',
24             lazy => 1,
25             clearer => '_clear_buffer',
26             default => sub { [] },
27             handles =>
28             {
29             has_buffer => 'count',
30             all_buffer => 'elements',
31             push_buffer => 'push',
32             shift_buffer => 'shift',
33             join_buffer => 'join',
34             }
35             );
36              
37              
38             has callback =>
39             (
40             is => 'ro',
41             isa => 'CodeRef',
42             lazy => 1,
43             default => sub { sub { Carp::confess('Parsing error happened: '. join("\n", @_)) } },
44             );
45              
46              
47             has handler =>
48             (
49             is => 'ro',
50             isa => 'POE::Filter::XML::Handler',
51             lazy => 1,
52             builder => '_build_handler',
53             );
54              
55              
56             has parser =>
57             (
58             is => 'ro',
59             isa => 'XML::LibXML',
60             lazy => 1,
61             builder => '_build_parser',
62             clearer => '_clear_parser'
63             );
64              
65              
66             has not_streaming =>
67             (
68             is => 'ro',
69             isa => 'Bool',
70             default => 0,
71             );
72              
73             sub _build_handler {
74             my ($self) = @_;
75             POE::Filter::XML::Handler->new(not_streaming => $self->not_streaming)
76             }
77              
78             sub _build_parser {
79             my ($self) = @_;
80             XML::LibXML->new(Handler => $self->handler)
81             }
82              
83              
84             sub BUILD {
85             my ($self) = @_;
86             if($self->has_buffer)
87             {
88             eval
89             {
90             $self->parser->parse_chunk($self->join_buffer("\n"));
91             1;
92             }
93             or do
94             {
95             my $err = $@ || 'Zombie Error';
96             $self->callback->($err);
97             };
98              
99             $self->_clear_buffer();
100             }
101             }
102              
103              
104             sub reset {
105              
106             my ($self) = @_;
107             $self->handler->reset();
108             $self->_clear_parser();
109             $self->_clear_buffer();
110             }
111              
112              
113             sub get_one_start {
114              
115             my ($self, $raw) = @_;
116              
117             if (defined $raw)
118             {
119             foreach my $raw_data (@$raw)
120             {
121             $self->push_buffer(split(/(?=\x0a?\x0d|\x0d\x0a?)/s, $raw_data));
122             }
123             }
124             }
125              
126              
127             sub get_one {
128              
129             my ($self) = @_;
130              
131             if($self->handler->has_finished_nodes())
132             {
133             return [$self->handler->get_finished_node()];
134              
135             }
136             else
137             {
138             while($self->has_buffer())
139             {
140             my $line = $self->shift_buffer();
141              
142             eval
143             {
144             $self->parser->parse_chunk($line);
145             1;
146             }
147             or do
148             {
149             my $err = $@ || 'Zombie error';
150             $self->callback->($err);
151             };
152              
153             if($self->handler->has_finished_nodes())
154             {
155             my $node = $self->handler->get_finished_node();
156              
157             if($node->stream_end() or $self->not_streaming)
158             {
159             $self->parser->parse_chunk('', 1);
160             $self->reset();
161             }
162              
163             return [$node];
164             }
165             }
166             return [];
167             }
168             }
169              
170              
171             sub put {
172             my ($self, $nodes) = @_;
173             my $output = [];
174              
175             foreach my $node (@$nodes)
176             {
177             if($node->stream_start())
178             {
179             $self->reset();
180             }
181             push(@$output, $node->toString());
182             }
183              
184             return $output;
185             }
186              
187             1;
188              
189              
190             =pod
191              
192             =head1 NAME
193              
194             POE::Filter::XML - XML parsing for the POE framework
195              
196             =head1 VERSION
197              
198             version 1.140700
199              
200             =head1 SYNOPSIS
201              
202             use POE::Filter::XML;
203             my $filter = POE::Filter::XML->new();
204              
205             my $wheel = POE::Wheel:ReadWrite->new(
206             Filter => $filter,
207             InputEvent => 'input_event',
208             );
209              
210             =head1 DESCRIPTION
211              
212             POE::Filter::XML provides POE with a completely encapsulated XML parsing
213             strategy for POE::Wheels that will be dealing with XML streams.
214              
215             The parser is XML::LibXML
216              
217             =head1 PUBLIC_ATTRIBUTES
218              
219             =head2 not_streaming
220              
221             is: ro, isa: Bool, default: false
222              
223             Setting the not_streaming attribute to true via new() will put this filter into
224             non-streaming mode, meaning that whole documents are parsed before nodes are
225             returned. This is handy for XMLRPC or other short documents.
226              
227             =head1 PRIVATE_ATTRIBUTES
228              
229             =head2 buffer
230              
231             is: ro, isa: ArrayRef, traits: Array
232              
233             buffer holds the raw data to be parsed. Raw data should be split on network
234             new lines before being added to the buffer. Access to this attribute is
235             provided by the following methods:
236              
237             handles =>
238             {
239             has_buffer => 'count',
240             all_buffer => 'elements',
241             push_buffer => 'push',
242             shift_buffer => 'shift',
243             join_buffer => 'join',
244             }
245              
246             =head2 callback
247              
248             is: ro, isa: CodeRef
249              
250             callback holds the CodeRef to be call in the event that there is an exception
251             generated while parsing content. By default it holds a CodeRef that simply
252             calls Carp::confess.
253              
254             =head2 handler
255              
256             is: ro, isa: POE::Filter::XML::Handler
257              
258             handler holds the SAX handler to be used for processing events from the parser.
259             By default POE::Filter::XML::Handler is instantiated and used.
260              
261             The L</not_streaming> attribute is passed to the constructor of Handler.
262              
263             =head2 parser
264              
265             is: ro, isa: XML::LibXML
266              
267             parser holds an instance of the XML::LibXML parser. The L</handler> attribute
268             is passed to the constructor of XML::LibXML.
269              
270             =head1 PUBLIC_METHODS
271              
272             =head2 get_one_start
273              
274             (ArrayRef $raw?)
275              
276             This method is part of the POE::Filter API. See L<POE::Filter/get_one_start>
277             for an explanation of its usage.
278              
279             =head2 get_one
280              
281             returns (ArrayRef)
282              
283             This method is part of the POE::Filter API. See L<POE::Filter/get_one> for an
284             explanation of its usage.
285              
286             =head2 put
287              
288             (ArrayRef $nodes) returns (ArrayRef)
289              
290             This method is part of the POE::Filter API. See L<POE::Filter/put> for an
291             explanation of its usage.
292              
293             =head1 PROTECTED_METHODS
294              
295             =head2 reset
296              
297             reset() is an internal method that gets called when either a stream_start(1)
298             POE::Filter::XML::Node gets placed into the filter via L</put>, or when a
299             stream_end(1) POE::Filter::XML::Node is pulled out of the queue of finished
300             Nodes via L</get_one>. This facilitates automagical behavior when using the
301             Filter within the XMPP protocol that requires many new stream initiations.
302             This method is also called after every document when not in streaming mode.
303             Useful for handling XMLRPC processing.
304              
305             This method really should never be called outside of the Filter, but it is
306             documented here in case the Filter is used outside of the POE context.
307              
308             =head1 PRIVATE_METHODS
309              
310             =head2 BUILD
311              
312             A BUILD method is provided to parse the initial buffer (if any was included
313             when constructing the filter).
314              
315             =head1 AUTHOR
316              
317             Nicholas R. Perez <nperez@cpan.org>
318              
319             =head1 COPYRIGHT AND LICENSE
320              
321             This software is copyright (c) 2014 by Nicholas R. Perez <nperez@cpan.org>.
322              
323             This is free software; you can redistribute it and/or modify it under
324             the same terms as the Perl 5 programming language system itself.
325              
326             =cut
327              
328              
329             __END__
330