File Coverage

blib/lib/POE/Filter/XML/Handler.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::Handler;
2             {
3             $POE::Filter::XML::Handler::VERSION = '1.140700';
4             }
5              
6             #ABSTRACT: Default SAX Handler for POE::Filter::XML
7              
8 1     1   4641 use Moose;
  0            
  0            
9             use MooseX::NonMoose;
10              
11             extends 'XML::SAX::Base';
12              
13             use POE::Filter::XML::Node;
14              
15              
16             has current_node =>
17             (
18             is => 'rw',
19             isa => 'POE::Filter::XML::Node',
20             predicate => '_has_current_node',
21             clearer => '_clear_current_node'
22             );
23              
24              
25             has finished_nodes =>
26             (
27             is => 'ro',
28             traits => ['Array'],
29             isa => 'ArrayRef',
30             default => sub { [] },
31             handles =>
32             {
33             all_finished_nodes => 'elements',
34             has_finished_nodes => 'count',
35             add_finished_node => 'push',
36             get_finished_node => 'shift',
37             _clear_finished_nodes => 'clear',
38             }
39             );
40              
41              
42             has depth_stack =>
43             (
44             is => 'ro',
45             traits => ['Array'],
46             isa => 'ArrayRef',
47             default => sub { [] },
48             clearer => '_clear_depth_stack',
49             handles =>
50             {
51             push_depth_stack => 'push',
52             pop_depth_stack => 'pop',
53             depth => 'count',
54             }
55             );
56              
57              
58             has not_streaming => ( is => 'ro', isa => 'Bool', default => 0 );
59              
60              
61             sub reset {
62             my ($self) = @_;
63             $self->_clear_current_node();
64             $self->_clear_finished_nodes();
65             $self->_clear_depth_stack();
66             }
67              
68              
69             sub start_element {
70             my ($self, $data) = @_;
71             my $node = POE::Filter::XML::Node->new($data->{'Name'});
72              
73             foreach my $attrib (values %{$data->{'Attributes'}})
74             {
75             $node->setAttribute
76             (
77             $attrib->{'Name'},
78             $attrib->{'Value'}
79             );
80             }
81              
82             if($self->depth() == 0)
83             {
84             #start of a document
85             $self->push_depth_stack($node);
86              
87             if($self->not_streaming)
88             {
89             $self->current_node($node);
90             }
91             else
92             {
93             $node->_set_stream_start(1);
94             $self->add_finished_node($node);
95             }
96             }
97             else
98             {
99             # Top level fragment
100             $self->push_depth_stack($self->current_node);
101              
102             if($self->depth() == 2)
103             {
104             if($self->not_streaming)
105             {
106             $self->current_node->appendChild($node);
107             }
108             $self->current_node($node);
109             }
110             else
111             {
112             # Some node within a fragment
113             $self->current_node->appendChild($node);
114             $self->current_node($node);
115             }
116             }
117              
118             $self->SUPER::start_element($data);
119             }
120              
121              
122             sub end_element {
123            
124             my ($self, $data) = @_;
125              
126             if($self->depth() == 1)
127             {
128             if($self->not_streaming)
129             {
130             $self->add_finished_node($self->pop_depth_stack());
131             }
132             else
133             {
134             my $end = POE::Filter::XML::Node->new($data->{'Name'});
135             $end->_set_stream_end(1);
136             $self->add_finished_node($end);
137             }
138             }
139             elsif($self->depth() == 2)
140             {
141             if($self->not_streaming)
142             {
143             $self->current_node($self->pop_depth_stack());
144             }
145             else
146             {
147             $self->add_finished_node($self->current_node);
148             $self->_clear_current_node();
149             $self->pop_depth_stack();
150             }
151             }
152             else
153             {
154             $self->current_node($self->pop_depth_stack());
155             }
156              
157             $self->SUPER::end_element($data);
158             }
159              
160              
161             sub characters {
162              
163             my ($self, $data) = @_;
164              
165             if($self->depth() == 1)
166             {
167             return;
168             }
169              
170             $self->current_node->appendText($data->{'Data'});
171              
172             $self->SUPER::characters($data);
173             }
174              
175             1;
176              
177              
178              
179             =pod
180              
181             =head1 NAME
182              
183             POE::Filter::XML::Handler - Default SAX Handler for POE::Filter::XML
184              
185             =head1 VERSION
186              
187             version 1.140700
188              
189             =head1 DESCRIPTION
190              
191             POE::Filter::XML::Handler is the default SAX handler for POE::Filter::XML. It
192             extends XML::SAX::Base to provide different semantics for streaming vs.
193             non-streaming contexts. This handle by default builds POE::Filter::XML::Nodes.
194              
195             =head1 PUBLIC_ATTRIBUTES
196              
197             =head2 not_streaming
198              
199             is: ro, isa: Bool, default: false
200              
201             not_streaming determines the behavior for the opening tag parsed. If what is
202             being parsed is not a stream, the document will be parsed in full then placed
203             into the finished_nodes attribute. Otherwise, the opening tag will be placed
204             immediately into the finished_nodes bucket.
205              
206             =head1 PRIVATE_ATTRIBUTES
207              
208             =head2 current_node
209              
210             is: rw, isa: POE::Filter::XML::Node
211              
212             current_node holds the node being immediately parsed.
213              
214             =head2 finished_nodes
215              
216             is: ro, isa: ArrayRef, traits: Array
217              
218             finished_nodes holds the nodes that have been completely parsed. Access to this
219             attribute is provided through the following methods:
220              
221             handles =>
222             {
223             all_finished_nodes => 'elements',
224             has_finished_nodes => 'count',
225             add_finished_node => 'push',
226             get_finished_node => 'shift',
227             }
228              
229             =head2 depth_stack
230              
231             is: ro, isa: ArrayRef, traits: Array
232              
233             depth_stack holds the operating stack for the parsed nodes. As nodes are
234             processed, ancendants of the current node are stored in the stack. When done
235             they are popped off the stack. Access to this attribute is provided through the
236             following methods:
237              
238             handles =>
239             {
240             push_depth_stack => 'push',
241             pop_depth_stack => 'pop',
242             depth => 'count',
243             }
244              
245             =head1 PUBLIC_METHODS
246              
247             =head2 reset
248              
249             reset will clear the current node, the finished nodes, and the depth stack.
250              
251             =head1 PROTECTED_METHODS
252              
253             =head2 override start_element
254              
255             (HashRef $data)
256              
257             start_element is overriden from the XML::SAX::Base class to provide our custom
258             behavior for dealing with streaming vs. non-streaming data. It builds Nodes
259             then attaches them to either the root node (non-streaming) or as stand-alone
260             top level fragments (streaming) sets them to the current node. Children nodes
261             are appended to their parents before getting set as the current node. Then the
262             base class method is called via super()
263              
264             =head2 override end_element
265              
266             (HashRef $data)
267              
268             end_element is overriden from the XML::SAX::Base class to provide our custom
269             behavior for dealing with streaming vs. non-streaming data. Mostly this method
270             is in charge of stack management when the depth of the stack reaches certain
271             points. In streaming documents, this means that top level fragments (not root)
272             are popped off the stack and added to the finished_nodes collection. Otherwise
273             a Node is created with stream_end set and added to the finished nodes.
274              
275             Then the base class method is called via super()
276              
277             =head2 override characters
278              
279             (HashRef $data)
280              
281             characters merely applies the character data as text to the current node being
282             processed. It then calls the base class method via super().
283              
284             =head1 AUTHOR
285              
286             Nicholas R. Perez <nperez@cpan.org>
287              
288             =head1 COPYRIGHT AND LICENSE
289              
290             This software is copyright (c) 2014 by Nicholas R. Perez <nperez@cpan.org>.
291              
292             This is free software; you can redistribute it and/or modify it under
293             the same terms as the Perl 5 programming language system itself.
294              
295             =cut
296              
297              
298             __END__
299