File Coverage

blib/lib/RDF/Trine/Iterator/SAXHandler.pm
Criterion Covered Total %
statement 98 123 79.6
branch 34 50 68.0
condition 1 4 25.0
subroutine 15 17 88.2
pod 8 8 100.0
total 156 202 77.2


line stmt bran cond sub pod time code
1             # RDF::Trine::Iterator::SAXHandler
2             # -----------------------------------------------------------------------------
3              
4             =head1 NAME
5              
6             RDF::Trine::Iterator::SAXHandler - SAX Handler for parsing SPARQL XML Results format
7              
8             =head1 VERSION
9              
10             This document describes RDF::Trine::Iterator::SAXHandler version 1.018
11              
12             =head1 STATUS
13              
14             This module's API and functionality should be considered unstable.
15             In the future, this module may change in backwards-incompatible ways,
16             or be removed entirely. If you need functionality that this module provides,
17             please L<get in touch|http://www.perlrdf.org/>.
18              
19             =head1 SYNOPSIS
20              
21             use RDF::Trine::Iterator::SAXHandler;
22             my $handler = RDF::Trine::Iterator::SAXHandler->new();
23             my $p = XML::SAX::ParserFactory->parser(Handler => $handler);
24             $p->parse_file( $string );
25             my $iter = $handler->iterator;
26              
27             =head1 METHODS
28              
29             Beyond the methods documented below, this class inherits methods from the
30             L<XML::SAX::Base> class.
31              
32             =over 4
33              
34             =cut
35              
36             package RDF::Trine::Iterator::SAXHandler;
37              
38 68     68   423 use strict;
  68         156  
  68         1730  
39 68     68   330 use warnings;
  68         147  
  68         1716  
40 68     68   333 use Scalar::Util qw(refaddr);
  68         153  
  68         2853  
41 68     68   554 use base qw(XML::SAX::Base);
  68         144  
  68         49796  
42              
43 68     68   851238 use Data::Dumper;
  68         341  
  68         3773  
44 68     68   29031 use RDF::Trine::VariableBindings;
  68         181  
  68         2575  
45              
46             our ($VERSION);
47             BEGIN {
48 68     68   53664 $VERSION = '1.018';
49             }
50              
51             my %strings;
52             my %tagstack;
53             my %results;
54             my %values;
55             my %bindings;
56             my %booleans;
57             my %variables;
58             my %has_head;
59             my %has_end;
60             my %result_count;
61             my %result_handlers;
62             my %config;
63              
64             my %expecting_string = map { $_ => 1 } qw(boolean bnode uri literal);
65              
66             =item C<< new ( [ \&handler ] ) >>
67              
68             Returns a new XML::SAX handler object. If C<< &handler >> is supplied, it will
69             be called with a variable bindings object as each is parsed, bypassing the
70             normal process of collecting the results for retrieval via an iterator object.
71              
72             =cut
73              
74             sub new {
75 3     3 1 7 my $class = shift;
76 3         23 my $self = $class->SUPER::new();
77 3 50       202 if (@_) {
78 0         0 my $addr = refaddr( $self );
79 0         0 my $code = shift;
80 0   0     0 my $args = shift || {};
81 0         0 $result_handlers{ $addr } = $code;
82 0         0 $config{ $addr } = { %$args };
83             }
84 3         9 return $self;
85             }
86              
87             =item C<< iterator >>
88              
89             Returns the RDF::Trine::Iterator object after parsing is complete.
90              
91             =cut
92              
93             sub iterator {
94 3     3 1 8 my $self = shift;
95 3         9 my $addr = refaddr( $self );
96            
97 3 100       13 if (exists( $booleans{ $addr })) {
98 1         4 return $self->iterator_class->new( [$booleans{ $addr }] );
99             } else {
100 2         6 my $vars = delete $variables{ $addr };
101 2         6 my $results = delete $results{ $addr };
102 2         12 return $self->iterator_class->new( $results, $vars );
103             }
104             }
105              
106             # =item C<< has_head >>
107             #
108             # Returns true if the <head/> element has been completely parsed, false otherwise.
109             #
110             # =cut
111             #
112             # sub has_head {
113             # my $self = shift;
114             # my $addr = refaddr( $self );
115             # return ($has_head{ $addr }) ? 1 : 0;
116             # }
117             #
118             # =item C<< has_end >>
119             #
120             # Returns true if the <sparql/> element (the entire iterator) has been completely
121             # parsed, false otherwise.
122             #
123             # =cut
124             #
125             # sub has_end {
126             # my $self = shift;
127             # my $addr = refaddr( $self );
128             # return ($has_end{ $addr }) ? 1 : 0;
129             # }
130              
131             =item C<< iterator_class >>
132              
133             Returns the iterator class appropriate for the parsed results (either
134             ::Iterator::Boolean or ::Iterator::Bindings).
135              
136             =cut
137              
138             sub iterator_class {
139 3     3 1 7 my $self = shift;
140 3         8 my $addr = refaddr( $self );
141 3 100       9 if (exists( $booleans{ $addr })) {
142 1         11 return 'RDF::Trine::Iterator::Boolean';
143             } else {
144 2         19 return 'RDF::Trine::Iterator::Bindings';
145             }
146             }
147              
148             =item C<< iterator_args >>
149              
150             Returns the arguments suitable for passing to the iterator constructor after
151             the iterator data.
152              
153             =cut
154              
155             sub iterator_args {
156 0     0 1 0 my $self = shift;
157 0         0 my $addr = refaddr( $self );
158            
159 0 0       0 if (exists( $booleans{ $addr })) {
160 0         0 return;
161             } else {
162 0         0 my $vars = $variables{ $addr };
163 0         0 return ($vars);
164             }
165             }
166              
167             =item C<< pull_result >>
168              
169             Returns the next result from the iterator, if available (if it has been parsed yet).
170             Otherwise, returns the empty list.
171              
172             =cut
173              
174             sub pull_result {
175 0     0 1 0 my $self = shift;
176 0         0 my $addr = refaddr( $self );
177            
178 0 0       0 if (exists( $booleans{ $addr })) {
179 0 0       0 if (exists($booleans{ $addr })) {
180 0         0 return [$booleans{ $addr }];
181             }
182             } else {
183 0 0       0 if (scalar(@{ $results{ $addr } || [] })) {
  0 0       0  
184 0         0 my $result = shift( @{ $results{ $addr } } );
  0         0  
185 0         0 return $result;
186             }
187             }
188 0         0 return;
189             }
190              
191             =begin private
192              
193             =item C<< start_element >>
194              
195             =cut
196              
197             sub start_element {
198 39     39 1 15838 my $self = shift;
199 39         64 my $el = shift;
200 39         68 my $tag = $el->{LocalName};
201 39         90 my $addr = refaddr( $self );
202            
203 39         63 unshift( @{ $tagstack{ $addr } }, [$tag, $el] );
  39         121  
204 39 100       138 if ($expecting_string{ $tag }) {
205 11         31 $strings{ $addr } = '';
206             }
207             }
208              
209             =item C<< end_element >>
210              
211             =cut
212              
213             sub end_element {
214 39     39 1 4440 my $self = shift;
215 39         70 my $class = ref($self);
216 39         55 my $eel = shift;
217 39         88 my $addr = refaddr( $self );
218 39         73 my $string = $strings{ $addr };
219 39         60 my $taginfo = shift( @{ $tagstack{ $addr } } );
  39         68  
220 39         76 my ($tag, $el) = @$taginfo;
221            
222 39 100       193 if ($tag eq 'head') {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
223 3         8 $has_head{ $addr } = 1;
224 3 50       15 if (my $code = $result_handlers{ $addr }) {
225 0 0       0 if ($config{ $addr }{ variables }) {
226 0         0 $code->( $variables{ $addr } );
227             }
228             }
229             } elsif ($tag eq 'sparql') {
230 3         11 $has_end{ $addr } = 1;
231             } elsif ($tag eq 'variable') {
232 5         13 push( @{ $variables{ $addr } }, $el->{Attributes}{'{}name'}{Value});
  5         22  
233             } elsif ($tag eq 'boolean') {
234 1 50       6 $booleans{ $addr } = ($string eq 'true') ? 1 : 0;
235             } elsif ($tag eq 'binding') {
236 10         21 my $name = $el->{Attributes}{'{}name'}{Value};
237 10         22 my $value = delete( $values{ $addr } );
238 10         34 $bindings{ $addr }{ $name } = $value;
239             } elsif ($tag eq 'result') {
240 4   50     15 my $result = delete( $bindings{ $addr } ) || {};
241 4         9 $result_count{ $addr }++;
242 4         21 my $vb = RDF::Trine::VariableBindings->new( $result );
243            
244 4 50       13 if (my $code = $result_handlers{ $addr }) {
245 0         0 $code->( $vb );
246             } else {
247 4         6 push( @{ $results{ $addr } }, $vb );
  4         18  
248             }
249             } elsif ($tag eq 'bnode') {
250 1         9 $values{ $addr } = RDF::Trine::Node::Blank->new( $string );
251             } elsif ($tag eq 'uri') {
252 3         24 $values{ $addr } = RDF::Trine::Node::Resource->new( $string );
253             } elsif ($tag eq 'literal') {
254 6         12 my ($lang, $dt);
255 6 100       22 if (my $dtinf = $el->{Attributes}{'{}datatype'}) {
    100          
256 1         3 $dt = $dtinf->{Value};
257             } elsif (my $langinf = $el->{Attributes}{'{http://www.w3.org/XML/1998/namespace}lang'}) {
258 1         3 $lang = $langinf->{Value};
259             }
260 6         33 $values{ $addr } = RDF::Trine::Node::Literal->new( $string, $lang, $dt );
261             }
262             }
263              
264             =item C<< characters >>
265              
266             =cut
267              
268             sub characters {
269 49     49 1 2514 my $self = shift;
270 49         73 my $data = shift;
271 49         108 my $addr = refaddr( $self );
272            
273 49         105 my $tag = $self->_current_tag;
274 49 100       151 if ($expecting_string{ $tag }) {
275 11         20 my $chars = $data->{Data};
276 11         33 $strings{ $addr } .= $chars;
277             }
278             }
279              
280             sub _current_tag {
281 49     49   72 my $self = shift;
282 49         94 my $addr = refaddr( $self );
283 49         105 return $tagstack{ $addr }[0][0];
284             }
285              
286             sub DESTROY {
287 3     3   26 my $self = shift;
288 3         10 my $addr = refaddr( $self );
289 3         8 delete $strings{ $addr };
290 3         6 delete $results{ $addr };
291 3         8 delete $tagstack{ $addr };
292 3         4 delete $values{ $addr };
293 3         6 delete $bindings{ $addr };
294 3         5 delete $booleans{ $addr };
295 3         6 delete $variables{ $addr };
296 3         5 delete $has_head{ $addr };
297 3         7 delete $has_end{ $addr };
298 3         5 delete $result_count{ $addr };
299 3         5 delete $result_handlers{ $addr };
300 3         35 delete $config{ $addr };
301             }
302              
303              
304             1;
305              
306             __END__
307              
308             =end private
309              
310             =back
311              
312             =head1 BUGS
313              
314             Please report any bugs or feature requests to through the GitHub web interface
315             at L<https://github.com/kasei/perlrdf/issues>.
316              
317             =head1 AUTHOR
318              
319             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
320              
321             =head1 COPYRIGHT
322              
323             Copyright (c) 2006-2012 Gregory Todd Williams. This
324             program is free software; you can redistribute it and/or modify it under
325             the same terms as Perl itself.
326              
327             =cut