File Coverage

blib/lib/RDF/Sesame/TableResult.pm
Criterion Covered Total %
statement 12 136 8.8
branch 0 74 0.0
condition n/a
subroutine 4 11 36.3
pod 5 5 100.0
total 21 226 9.2


line stmt bran cond sub pod time code
1             package RDF::Sesame::TableResult;
2              
3 1     1   2922 use strict;
  1         3  
  1         36  
4 1     1   5 use warnings;
  1         2  
  1         29  
5              
6 1     1   6 use base qw( Data::Table );
  1         1  
  1         1600  
7              
8 1     1   60194 use Carp;
  1         3  
  1         1818  
9              
10             our $VERSION = '0.17';
11              
12             #
13             # The $response parameter is an RDF::Sesame::Response object.
14             #
15             # This method is only intended to be called from RDF::Sesame::Repository
16             #
17             sub new {
18 0     0 1   my ($class, $r, %opts) = @_;
19              
20             # set our 'strip_' values
21 0           my $strip_literals = 0;
22 0           my $strip_uris = 0;
23 0           $strip_literals = $opts{strip} =~ /^literals|all$/;
24 0           $strip_uris = $opts{strip} =~ /^urirefs|all$/;
25              
26 0 0         my ( $content, $parser )
    0          
27             = $r->is_xml() ? ( $r->parsed_xml(), \&_parse_xml )
28             : $r->is_binary_results() ? ( $r->content(), \&_parse_bin )
29             : croak "TableResults: Bad response format"
30             ;
31 0           my ($column_names, $tuples) = $parser->(
32             $content,
33             $strip_literals,
34             $strip_uris,
35             );
36              
37 0           my $self = $class->SUPER::new($tuples, $column_names, 0);
38 0           $self->{coming} = 0; # the number of the next row for each()
39              
40             # rebless ourselves and return
41 0           return bless $self, $class;
42             }
43              
44             # Converts the binary RDF table results format from Sesame into an array of
45             # arrays with column names. The binary results format is described at
46             # http://www.openrdf.org/doc/sesame/api/org/openrdf/sesame/query/BinaryTableResultConstants.html
47             sub _parse_bin {
48 0     0     my ($bin, $strip_literals, $strip_uris) = @_;
49              
50             # validate the header
51 0           my ( $magic, $version, $column_count ) = unpack( 'A4 N N', $bin );
52 0 0         die "Not an Binary RDF Table Result" if $magic ne 'BRTR';
53 0 0         die "Version $version is higher than 1" if $version > 1;
54 0           substr( $bin, 0, 12, q{} );
55              
56             # collect the column names
57 0           my @column_names;
58 0           for ( 1 .. $column_count ) {
59 0           my ( $byte_count, $column_name ) = unpack( 'n X2 n/A*', $bin );
60 0           substr( $bin, 0, 2 + $byte_count, q{} );
61 0           push @column_names, $column_name;
62             }
63              
64             # parse the results table from the binary representation
65 0           my @row;
66             my @rows;
67 0           my $prev_row;
68 0           my @namespaces;
69             ROW:
70 0           while (1) {
71 0           my $column_value;
72 0           my $column_i = 0;
73 0           @row = ();
74              
75             COLUMN:
76 0           while ( $column_i < $column_count ) {
77 0           my ($record_type) = unpack( 'c', $bin );
78 0           substr( $bin, 0, 1, q{} );
79              
80 0 0         if ( $record_type == 0 ) { # NULL
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
81 0           $column_value = undef;
82             }
83             elsif ( $record_type == 1 ) { # REPEAT
84 0           $column_value = $prev_row->[$column_i];
85             }
86             elsif ( $record_type == 2 ) { # NAMESPACE
87 0           my ( $ns_id, $ns_len, $ns ) = unpack( 'N n X2 n/A*', $bin );
88 0           substr( $bin, 0, 6 + $ns_len, q{} );
89 0           $namespaces[$ns_id] = $ns;
90 0           redo COLUMN;
91             }
92             elsif ( $record_type == 3 ) { # QNAME
93 0           my ( $ns_id, $local_len, $local )
94             = unpack( 'N n X2 n/A*', $bin );
95 0           substr( $bin, 0, 6 + $local_len, q{} );
96 0           my $ns = $namespaces[$ns_id];
97 0 0         $column_value = $strip_uris ? "$ns$local" : "<$ns$local>";
98             }
99             elsif ( $record_type == 4 ) { # URI
100 0           my ( $uri_len, $uri ) = unpack( 'n X2 n/A*', $bin );
101 0           substr( $bin, 0, 2 + $uri_len, q{} );
102 0 0         $column_value = $strip_uris ? $uri : "<$uri>";
103             }
104             elsif ( $record_type == 5 ) { # BNODE
105 0           my ( $bnode_len, $bnode ) = unpack( 'n X2 n/A*', $bin );
106 0           substr( $bin, 0, 2 + $bnode_len, q{} );
107 0           $column_value = "_:$bnode";
108             }
109             elsif ( $record_type == 6 ) { # PLAIN LITERAL
110 0           my ( $lit_len, $lit ) = unpack( 'n X2 n/A*', $bin );
111 0           substr( $bin, 0, 2 + $lit_len, q{} );
112 0 0         $column_value = $strip_literals ? $lit : qq{"$lit"};
113             }
114             elsif ( $record_type == 7 ) { # LANG LITERAL
115 0           my ( $lit_len, $lit, $lang_len, $lang )
116             = unpack( 'n X2 n/A* n X2 n/A*', $bin );
117 0           substr( $bin, 0, 4 + $lit_len + $lang_len, q{} );
118 0 0         $column_value = $strip_literals ? $lit : qq{"$lit"\@$lang};
119             }
120             elsif ( $record_type == 8 ) { # DATATYPE LITERAL
121 0           my ( $lit_len, $lit, $record_type )
122             = unpack( 'n X2 n/A* c', $bin );
123 0           substr( $bin, 0, 3 + $lit_len, q{} );
124              
125 0           my $datatype;
126 0 0         if ( $record_type == 3 ) { # embedded QNAME
    0          
127 0           my ( $ns_id, $local_len, $local )
128             = unpack( 'N n X2 n/A*', $bin );
129 0           substr( $bin, 0, 6 + $local_len, q{} );
130 0           my $ns = $namespaces[$ns_id];
131 0           $datatype = $ns . $local;
132             }
133             elsif ( $record_type == 4 ) { # embedded URI
134 0           my ( $uri_len, $uri ) = unpack( 'n X2 n/A*', $bin );
135 0           substr( $bin, 0, 2 + $uri_len, q{} );
136 0           $datatype = $uri;
137             }
138             else {
139 0           die "Bad record type $record_type after typed literal";
140             }
141              
142 0 0         $column_value = $strip_literals ? $lit : qq{"$lit"^^<$datatype>};
143             }
144             elsif ( $record_type == 126 ) { # ERROR
145 0           my ( $error_type, $error_len, $error )
146             = unpack( 'c n X2 n/A*', $bin );
147 0           substr( $bin, 0, 3 + $error_len, q{} );
148 0           die "$error_type: $error";
149             }
150             elsif ( $record_type == 127 ) { # END OF RESULTS
151 0           last ROW;
152             }
153             else {
154 0           die "Unknown record type: $record_type";
155             }
156             }
157             continue {
158 0           push @row, $column_value;
159 0           $column_i++;
160             }
161              
162             }
163             continue {
164 0           my $row_copy = [ @row ];
165 0           push @rows, $row_copy;
166 0           $prev_row = $row_copy;
167             }
168              
169 0 0         push @rows, \@row if @row;
170              
171 0           return ( \@column_names, \@rows );
172             }
173              
174             # Converts the XML parse tree from RDF::Sesame::Response into
175             # an array of arrays with column names
176             sub _parse_xml {
177 0     0     my ( $parsed_xml, $strip_literals, $strip_uris ) = @_;
178              
179             # make a copy of the header info for ourselves
180 0           my @head = @{ $parsed_xml->{header}{columnName} };
  0            
181              
182             # convert the tuples into our internal representation
183 0           my @tuples;
184 0           foreach my $t ( @{ $parsed_xml->{tuple} } ) {
  0            
185 0           my @row = ();
186 0           foreach my $a ( @{ $t->{attribute} } ) {
  0            
187 0           my $content = $a->{content};
188              
189             # encode each type according to N-Triples syntax
190 0 0         if( $a->{type} eq 'bNode' ) {
    0          
    0          
191 0           push(@row, "_:$content");
192             } elsif( $a->{type} eq 'uri' ) {
193 0 0         if( $strip_uris ) {
194 0           push(@row, $content);
195             } else {
196 0           push(@row, "<$content>");
197             }
198             } elsif( $a->{type} eq 'literal' ) {
199 0 0         if( $strip_literals ) {
    0          
    0          
200 0           push(@row, $content);
201             } elsif( $a->{'xml:lang'} ) {
202 0           push(@row, "\"$content\"\@" . $a->{'xml:lang'} );
203             } elsif( $a->{datatype} ) {
204 0           push(@row, "\"$content\"^^<" . $a->{datatype} . ">" );
205             } else {
206 0           push(@row, "\"$content\"" );
207             }
208             } else {
209             # type must be 'null'
210 0           push(@row, undef);
211             }
212             }
213              
214 0           push(@tuples, \@row);
215             }
216              
217 0           return ( \@head, \@tuples );
218             }
219              
220             sub has_rows {
221 0     0 1   my $self = shift;
222              
223 0           return $self->nofRow > 0;
224             }
225              
226             sub sort {
227 0     0 1   my ($self, @ps) = @_;
228              
229 0           my $i = 1;
230 0           while( $i < $#_ ) {
231              
232             # munge the type parameter
233 0 0         if( defined $ps[$i] ) {
234 0 0         if( $ps[$i] eq 'numeric' ) {
    0          
235 0           $ps[$i] = 0;
236             } elsif( $ps[$i] eq 'non-numeric' ) {
237 0           $ps[$i] = 1;
238             }
239             }
240              
241 0           $i++;
242              
243             # munge the order parameter
244 0 0         if( defined $ps[$i] ) {
245 0 0         if( $ps[$i] eq 'asc' ) {
    0          
246 0           $ps[$i] = 0;
247             } elsif( $ps[$i] eq 'desc' ) {
248 0           $ps[$i] = 1;
249             }
250             }
251              
252 0           $i += 2; # skip the next colID parameter
253             }
254              
255 0           $self->SUPER::sort(@ps);
256             }
257              
258             sub each {
259 0     0 1   my ($self) = @_;
260              
261             # have we passed the last row?
262 0 0         if( $self->{coming} >= $self->nofRow ) {
263 0           $self->{coming} = 0;
264 0           return ();
265             }
266              
267             # nope, so return the current row and increment our pointer
268 0           return @{ $self->rowRef($self->{coming}++) };
  0            
269             }
270              
271             sub reset {
272 0     0 1   $_[0]->{coming} = 0;
273             }
274              
275              
276             1;
277              
278             __END__