File Coverage

blib/lib/RDF/Trine/Iterator/Graph.pm
Criterion Covered Total %
statement 139 177 78.5
branch 15 32 46.8
condition 9 16 56.2
subroutine 27 28 96.4
pod 11 11 100.0
total 201 264 76.1


line stmt bran cond sub pod time code
1             # RDF::Trine::Iterator::Graph
2             # -----------------------------------------------------------------------------
3              
4             =head1 NAME
5              
6             RDF::Trine::Iterator::Graph - Iterator class for graph query results
7              
8             =head1 VERSION
9              
10             This document describes RDF::Trine::Iterator::Graph version 1.018
11              
12             =head1 SYNOPSIS
13              
14             use RDF::Trine::Iterator::Graph;
15            
16             my $iterator = RDF::Trine::Iterator::Graph->new( \&data );
17             while (my $st = $iterator->next) {
18             # $st is a RDF::Trine::Statement object
19             print $st->as_string;
20             }
21              
22             =head1 METHODS
23              
24             Beyond the methods documented below, this class inherits methods from the
25             L<RDF::Trine::Iterator> class.
26              
27             =over 4
28              
29             =cut
30              
31             package RDF::Trine::Iterator::Graph;
32              
33 68     68   404 use strict;
  68         146  
  68         1698  
34 68     68   317 use warnings;
  68         132  
  68         1507  
35 68     68   306 no warnings 'redefine';
  68         142  
  68         1581  
36              
37 68     68   317 use JSON;
  68         136  
  68         357  
38 68     68   5913 use List::Util qw(max);
  68         169  
  68         3549  
39 68     68   435 use Scalar::Util qw(blessed);
  68         147  
  68         2663  
40              
41 68     68   382 use RDF::Trine::Iterator qw(sgrep);
  68         159  
  68         3052  
42 68     68   23675 use RDF::Trine::Iterator::Graph::Materialized;
  68         173  
  68         1655  
43              
44 68     68   385 use base qw(RDF::Trine::Iterator);
  68         138  
  68         5220  
45              
46             ######################################################################
47              
48             our ($VERSION);
49             BEGIN {
50 68     68   25036 $VERSION = '1.018';
51             }
52              
53             ######################################################################
54              
55              
56             =item C<new ( \@results, %args )>
57              
58             =item C<new ( \&results, %args )>
59              
60             Returns a new SPARQL Result interator object. Results must be either
61             an reference to an array containing results or a CODE reference that
62             acts as an iterator, returning successive items when called, and
63             returning undef when the iterator is exhausted.
64              
65             $type should be one of: bindings, boolean, graph.
66              
67             =cut
68              
69             sub new {
70 4263     4263 1 13651 my $class = shift;
71 4263   100 279   11848 my $stream = shift || sub { undef };
  279         576  
72             # Carp::confess unless (scalar(@_) % 2 == 0);
73 4263         9729 my %args = @_;
74            
75 4263         7358 my $type = 'graph';
76 4263         17972 return $class->SUPER::new( $stream, $type, [], %args );
77             }
78              
79             sub _new {
80 39     39   91 my $class = shift;
81 39         71 my $stream = shift;
82 39         77 my $type = shift;
83 39         67 my $names = shift;
84 39         106 my %args = @_;
85 39         122 return $class->new( $stream, %args );
86             }
87              
88             =item C<< as_bindings ( $s, $p, $o ) >>
89              
90             Returns the iterator as a Bindings iterator, using the supplied triple nodes to
91             determine the variable bindings.
92              
93             =cut
94              
95             sub as_bindings {
96 3     3 1 15 my $self = shift;
97 3         8 my @nodes = @_;
98 3         10 my @names = qw(subject predicate object context);
99 3         7 my %bindings;
100 3         11 foreach my $i (0 .. $#names) {
101 12 100 66     35 if (not($nodes[ $i ]) or not($nodes[ $i ]->isa('RDF::Trine::Node::Variable'))) {
102 9         37 $nodes[ $i ] = RDF::Trine::Node::Variable->new( $names[ $i ] );
103             }
104             }
105 3         11 foreach my $i (0 .. $#nodes) {
106 12         20 my $n = $nodes[ $i ];
107 12         29 $bindings{ $n->name } = $names[ $i ];
108             }
109 3         9 my $context = $nodes[ 3 ]->name;
110            
111             my $sub = sub {
112 3     3   15 my $statement = $self->next;
113 3 50       11 return unless ($statement);
114             my %values = map {
115 9         17 my $method = $bindings{ $_ };
116 9         30 $_ => $statement->$method()
117 3 50       12 } grep { ($statement->isa('RDF::Trine::Statement::Quad')) ? 1 : ($_ ne $context) } (keys %bindings);
  12         60  
118 3         11 return \%values;
119 3         13 };
120 3         33 return RDF::Trine::Iterator::Bindings->new( $sub, [ keys %bindings ] );
121             }
122              
123             =item C<< materialize >>
124              
125             Returns a materialized version of the current graph iterator.
126             The materialization process will leave this iterator empty. The materialized
127             iterator that is returned should be used for any future need for the iterator's
128             data.
129              
130             =cut
131              
132             sub materialize {
133 2     2 1 8 my $self = shift;
134 2         54 my @data = $self->get_all;
135 2         8 my @args = $self->construct_args;
136 2         9 return $self->_mclass->_new( \@data, @args );
137             }
138              
139             sub _mclass {
140 2     2   25 return 'RDF::Trine::Iterator::Graph::Materialized';
141             }
142              
143              
144             =item C<< unique >>
145              
146             Returns a Graph iterator that ensures the returned statements are unique. While
147             the underlying RDF graph is the same regardless of uniqueness, the iterator's
148             serialization methods assume the results are unique, and so use this method
149             before serialization.
150              
151             Uniqueness is opt-in for efficiency concerns -- this method requires O(n) memory,
152             and so may have noticeable effects on large graphs.
153              
154             =cut
155              
156             sub unique {
157 13     13 1 27 my $self = shift;
158 13         22 my %seen;
159 68     68   481 no warnings 'uninitialized';
  68         146  
  68         58681  
160             my $stream = sgrep( sub {
161 27     27   42 my $s = $_;
162 27         75 my $str = $s->as_string;
163 27         115 not($seen{ $str }++)
164 13         68 }, $self);
165 13         36 return $stream;
166             }
167              
168             =item C<is_graph>
169              
170             Returns true if the underlying result is an RDF graph.
171              
172             =cut
173              
174             sub is_graph {
175 1     1 1 769 my $self = shift;
176 1         6 return 1;
177             }
178              
179             =item C<as_string ( $max_size [, \$count] )>
180              
181             Returns a string table serialization of the stream data.
182              
183             =cut
184              
185             sub as_string {
186 0     0 1 0 my $self = shift;
187 0   0     0 my $max_result_size = shift || 0;
188 0         0 my $rescount = shift;
189 0         0 my @names = qw(subject predicate object);
190 0         0 my $headers = \@names;
191 0         0 my @rows;
192 0         0 my $count = 0;
193 0         0 while (my $row = $self->next) {
194 0 0       0 push(@rows, [ map { blessed($_) ? $_->as_string : '' } map { $row->$_() } qw(subject predicate object) ]);
  0         0  
  0         0  
195 0 0 0     0 last if ($max_result_size and ++$count >= $max_result_size);
196             }
197             # my $rows = [ map { [ map { blessed($_) ? $_->as_string : '' } @{$_}{ @names } ] } @nodes ];
198 0 0       0 if (ref($rescount)) {
199 0         0 $$rescount = scalar(@rows);
200             }
201            
202 0         0 my @rule = qw(- +);
203 0         0 my @headers = (\q"| ");
204 0         0 push(@headers, map { $_ => \q" | " } @$headers);
  0         0  
205 0         0 pop @headers;
206 0         0 push @headers => (\q" |");
207            
208 0 0       0 if ('ARRAY' eq ref $rows[0]) {
209 0 0       0 if (@$headers == @{ $rows[0] }) {
  0         0  
210 0         0 my $table = Text::Table->new(@headers);
211 0         0 $table->rule(@rule);
212 0         0 $table->body_rule(@rule);
213 0         0 $table->load(@rows);
214            
215             return join('',
216             $table->rule(@rule),
217             $table->title,
218             $table->rule(@rule),
219 0         0 map({ $table->body($_) } 0 .. @rows),
  0         0  
220             $table->rule(@rule)
221             );
222             } else {
223 0         0 die("make_table() rows must be an AoA with rows being same size as headers");
224             }
225             } else {
226 0         0 return '';
227             }
228             }
229              
230             =item C<as_xml ( $max_size )>
231              
232             Returns an XML serialization of the stream data.
233              
234             =cut
235              
236             sub as_xml {
237 6     6 1 18 my $self = shift;
238 6   100     22 my $max_result_size = shift || 0;
239 6         10 my $string = '';
240 6     2   118 open( my $fh, '>', \$string );
  2         15  
  2         3  
  2         13  
241 6         1404 $self->print_xml( $fh, $max_result_size );
242 6         47 return $string;
243             }
244              
245             =item C<< print_xml ( $fh, $max_size ) >>
246              
247             Prints an XML serialization of the stream data to the filehandle $fh.
248              
249             =cut
250              
251             sub print_xml {
252 6     6 1 11 my $self = shift;
253 6         10 my $fh = shift;
254 6   100     25 my $max_result_size = shift || 0;
255 6         16 my $graph = $self->unique();
256            
257 6         10 my $count = 0;
258 6         10 print {$fh} <<"END";
  6         20  
259             <?xml version="1.0" encoding="utf-8"?>
260             <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
261             END
262 6         22 while (my $stmt = $graph->next) {
263 9 100       22 if ($max_result_size) {
264 4 100       12 last if ($count++ >= $max_result_size);
265             }
266 8         24 my $p = $stmt->predicate->uri_value;
267 8         51 my $pos = max( rindex( $p, '/' ), rindex( $p, '#' ) );
268 8         25 my $ns = substr($p,0,$pos+1);
269 8         18 my $local = substr($p, $pos+1);
270 8         20 my $subject = $stmt->subject;
271 8 50       34 my $subjstr = ($subject->is_resource)
272             ? 'rdf:about="' . $subject->uri_value . '"'
273             : 'rdf:nodeID="' . $subject->blank_identifier . '"';
274 8         22 my $object = $stmt->object;
275            
276 8         12 print {$fh} qq[<rdf:Description $subjstr>\n];
  8         32  
277 8 100       27 if ($object->is_resource) {
    50          
278 5         13 my $uri = $object->uri_value;
279 5         8 print {$fh} qq[\t<${local} xmlns="${ns}" rdf:resource="$uri"/>\n];
  5         19  
280             } elsif ($object->isa('RDF::Trine::Node::Blank')) {
281 0         0 my $id = $object->blank_identifier;
282 0         0 print {$fh} qq[\t<${local} xmlns="${ns}" rdf:nodeID="$id"/>\n];
  0         0  
283             } else {
284 3         10 my $value = $object->literal_value;
285             # escape < and & and ' and " and >
286 3         7 $value =~ s/&/&amp;/g;
287 3         7 $value =~ s/'/&apos;/g;
288 3         10 $value =~ s/"/&quot;/g;
289 3         7 $value =~ s/</&lt;/g;
290 3         5 $value =~ s/>/&gt;/g;
291            
292 3         7 my $tag = qq[${local} xmlns="${ns}"];
293 3 50       10 if (defined($object->literal_value_language)) {
    50          
294 0         0 my $lang = $object->literal_value_language;
295 0         0 $tag .= qq[ xml:lang="${lang}"];
296             } elsif (defined($object->literal_datatype)) {
297 0         0 my $dt = $object->literal_datatype;
298 0         0 $tag .= qq[ rdf:datatype="${dt}"];
299             }
300 3         5 print {$fh} qq[\t<${tag}>${value}</${local}>\n];
  3         12  
301             }
302 8         17 print {$fh} qq[</rdf:Description>\n];
  8         33  
303             }
304 6         9 print {$fh} "</rdf:RDF>\n";
  6         68  
305             }
306              
307             =item C<as_json ( $max_size )>
308              
309             Returns a JSON serialization of the stream data.
310              
311             =cut
312              
313             sub as_json {
314 1     1 1 70 my $self = shift;
315 1         3 my $max_result_size = shift;
316 1         17 throw RDF::Trine::Error::SerializationError ( -text => 'There is no JSON serialization specified for graph query results' );
317             }
318              
319             =item C<< as_hashref >>
320              
321             Returns a hashref representing the model in an RDF/JSON-like manner.
322              
323             See C<< as_hashref >> at L<RDF::Trine::Model> for full documentation of the
324             hashref format.
325              
326             =cut
327              
328             sub as_hashref {
329 2     2 1 5 my $self = shift;
330 2         5 my $index = {};
331 2         8 while (my $statement = $self->next) {
332            
333 6 50       20 my $s = $statement->subject->isa('RDF::Trine::Node::Blank') ?
334             ('_:'.$statement->subject->blank_identifier) :
335             $statement->subject->uri ;
336 6         17 my $p = $statement->predicate->uri ;
337 6         12 push @{ $index->{$s}->{$p} }, $statement->object->as_hashref;
  6         25  
338             }
339 2         11 return $index;
340             }
341              
342             =item C<< construct_args >>
343              
344             Returns the arguments necessary to pass to the stream constructor _new
345             to re-create this stream (assuming the same closure as the first
346              
347             =cut
348              
349             sub construct_args {
350 39     39 1 85 my $self = shift;
351 39         153 my $type = $self->type;
352 39   50     140 my $args = $self->_args || {};
353 39         100 return ($type, [], %{ $args });
  39         165  
354             }
355              
356              
357             1;
358              
359             __END__
360              
361             =back
362              
363             =head1 DEPENDENCIES
364              
365             L<JSON|JSON>
366              
367             L<Scalar::Util|Scalar::Util>
368              
369             =head1 BUGS
370              
371             Please report any bugs or feature requests to through the GitHub web interface
372             at L<https://github.com/kasei/perlrdf/issues>.
373              
374             =head1 AUTHOR
375              
376             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
377              
378             =head1 COPYRIGHT
379              
380             Copyright (c) 2006-2012 Gregory Todd Williams. This
381             program is free software; you can redistribute it and/or modify it under
382             the same terms as Perl itself.
383              
384             =cut