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.017
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   449 use strict;
  68         155  
  68         1672  
34 68     68   317 use warnings;
  68         142  
  68         1542  
35 68     68   312 no warnings 'redefine';
  68         137  
  68         1598  
36              
37 68     68   323 use JSON;
  68         140  
  68         363  
38 68     68   6217 use List::Util qw(max);
  68         167  
  68         3788  
39 68     68   458 use Scalar::Util qw(blessed);
  68         203  
  68         2767  
40              
41 68     68   388 use RDF::Trine::Iterator qw(sgrep);
  68         144  
  68         3039  
42 68     68   24019 use RDF::Trine::Iterator::Graph::Materialized;
  68         175  
  68         1667  
43              
44 68     68   433 use base qw(RDF::Trine::Iterator);
  68         149  
  68         4919  
45              
46             ######################################################################
47              
48             our ($VERSION);
49             BEGIN {
50 68     68   25977 $VERSION = '1.017';
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 18289 my $class = shift;
71 4263   100 279   12392 my $stream = shift || sub { undef };
  279         621  
72             # Carp::confess unless (scalar(@_) % 2 == 0);
73 4263         9854 my %args = @_;
74            
75 4263         7657 my $type = 'graph';
76 4263         19324 return $class->SUPER::new( $stream, $type, [], %args );
77             }
78              
79             sub _new {
80 39     39   73 my $class = shift;
81 39         78 my $stream = shift;
82 39         67 my $type = shift;
83 39         70 my $names = shift;
84 39         86 my %args = @_;
85 39         107 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 17 my $self = shift;
97 3         16 my @nodes = @_;
98 3         11 my @names = qw(subject predicate object context);
99 3         9 my %bindings;
100 3         13 foreach my $i (0 .. $#names) {
101 12 100 66     44 if (not($nodes[ $i ]) or not($nodes[ $i ]->isa('RDF::Trine::Node::Variable'))) {
102 9         44 $nodes[ $i ] = RDF::Trine::Node::Variable->new( $names[ $i ] );
103             }
104             }
105 3         11 foreach my $i (0 .. $#nodes) {
106 12         25 my $n = $nodes[ $i ];
107 12         30 $bindings{ $n->name } = $names[ $i ];
108             }
109 3         10 my $context = $nodes[ 3 ]->name;
110            
111             my $sub = sub {
112 3     3   18 my $statement = $self->next;
113 3 50       13 return unless ($statement);
114             my %values = map {
115 9         22 my $method = $bindings{ $_ };
116 9         39 $_ => $statement->$method()
117 3 50       14 } grep { ($statement->isa('RDF::Trine::Statement::Quad')) ? 1 : ($_ ne $context) } (keys %bindings);
  12         72  
118 3         13 return \%values;
119 3         17 };
120 3         37 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         15 my @data = $self->get_all;
135 2         9 my @args = $self->construct_args;
136 2         8 return $self->_mclass->_new( \@data, @args );
137             }
138              
139             sub _mclass {
140 2     2   20 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 28 my $self = shift;
158 13         25 my %seen;
159 68     68   474 no warnings 'uninitialized';
  68         152  
  68         61227  
160             my $stream = sgrep( sub {
161 27     27   46 my $s = $_;
162 27         74 my $str = $s->as_string;
163 27         107 not($seen{ $str }++)
164 13         71 }, $self);
165 13         33 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 873 my $self = shift;
176 1         5 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 25 my $self = shift;
238 6   100     24 my $max_result_size = shift || 0;
239 6         14 my $string = '';
240 6     2   123 open( my $fh, '>', \$string );
  2         14  
  2         4  
  2         13  
241 6         1454 $self->print_xml( $fh, $max_result_size );
242 6         51 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 13 my $self = shift;
253 6         11 my $fh = shift;
254 6   100     29 my $max_result_size = shift || 0;
255 6         19 my $graph = $self->unique();
256            
257 6         11 my $count = 0;
258 6         12 print {$fh} <<"END";
  6         25  
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         24 while (my $stmt = $graph->next) {
263 9 100       25 if ($max_result_size) {
264 4 100       11 last if ($count++ >= $max_result_size);
265             }
266 8         24 my $p = $stmt->predicate->uri_value;
267 8         59 my $pos = max( rindex( $p, '/' ), rindex( $p, '#' ) );
268 8         26 my $ns = substr($p,0,$pos+1);
269 8         21 my $local = substr($p, $pos+1);
270 8         22 my $subject = $stmt->subject;
271 8 50       38 my $subjstr = ($subject->is_resource)
272             ? 'rdf:about="' . $subject->uri_value . '"'
273             : 'rdf:nodeID="' . $subject->blank_identifier . '"';
274 8         27 my $object = $stmt->object;
275            
276 8         17 print {$fh} qq[<rdf:Description $subjstr>\n];
  8         33  
277 8 100       25 if ($object->is_resource) {
    50          
278 5         15 my $uri = $object->uri_value;
279 5         9 print {$fh} qq[\t<${local} xmlns="${ns}" rdf:resource="$uri"/>\n];
  5         20  
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         10 $value =~ s/&/&amp;/g;
287 3         7 $value =~ s/'/&apos;/g;
288 3         14 $value =~ s/"/&quot;/g;
289 3         6 $value =~ s/</&lt;/g;
290 3         7 $value =~ s/>/&gt;/g;
291            
292 3         10 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         7 print {$fh} qq[\t<${tag}>${value}</${local}>\n];
  3         17  
301             }
302 8         16 print {$fh} qq[</rdf:Description>\n];
  8         34  
303             }
304 6         11 print {$fh} "</rdf:RDF>\n";
  6         29  
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 77 my $self = shift;
315 1         3 my $max_result_size = shift;
316 1         18 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 7 my $self = shift;
330 2         5 my $index = {};
331 2         9 while (my $statement = $self->next) {
332            
333 6 50       19 my $s = $statement->subject->isa('RDF::Trine::Node::Blank') ?
334             ('_:'.$statement->subject->blank_identifier) :
335             $statement->subject->uri ;
336 6         18 my $p = $statement->predicate->uri ;
337 6         10 push @{ $index->{$s}->{$p} }, $statement->object->as_hashref;
  6         33  
338             }
339 2         10 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 77 my $self = shift;
351 39         150 my $type = $self->type;
352 39   50     147 my $args = $self->_args || {};
353 39         90 return ($type, [], %{ $args });
  39         155  
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