File Coverage

blib/lib/RDF/Trine/Store/Memory.pm
Criterion Covered Total %
statement 350 357 98.0
branch 85 90 94.4
condition 15 17 88.2
subroutine 33 35 94.2
pod 11 11 100.0
total 494 510 96.8


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             RDF::Trine::Store::Memory - Simple in-memory RDF store
4              
5             =head1 VERSION
6              
7             This document describes RDF::Trine::Store::Memory version 1.018
8              
9             =head1 SYNOPSIS
10              
11             use RDF::Trine::Store::Memory;
12              
13             =head1 DESCRIPTION
14              
15             RDF::Trine::Store::Memory provides an in-memory triple-store.
16              
17             =cut
18              
19             package RDF::Trine::Store::Memory;
20              
21 68     68   390 use strict;
  68         147  
  68         1579  
22 68     68   348 use warnings;
  68         132  
  68         1454  
23 68     68   305 no warnings 'redefine';
  68         130  
  68         1784  
24 68     68   332 use base qw(RDF::Trine::Store);
  68         140  
  68         4117  
25              
26 68     68   377 use Encode;
  68         154  
  68         4202  
27 68     68   26667 use Set::Scalar;
  68         555506  
  68         2563  
28 68     68   495 use Data::Dumper;
  68         147  
  68         2637  
29 68     68   30505 use Digest::SHA qw(sha1);
  68         171833  
  68         4808  
30 68     68   579 use List::Util qw(first);
  68         151  
  68         5387  
31 68     68   1026 use Scalar::Util qw(refaddr reftype blessed);
  68         201  
  68         2949  
32 68     68   24629 use RDF::Trine::Statement::Quad;
  68         220  
  68         2048  
33              
34 68     68   495 use RDF::Trine qw(iri);
  68         149  
  68         2818  
35 68     68   390 use RDF::Trine::Error;
  68         138  
  68         460  
36              
37             ######################################################################
38              
39             my @pos_names;
40             our $VERSION;
41             BEGIN {
42 68     68   5500 $VERSION = "1.018";
43 68         155 my $class = __PACKAGE__;
44 68         175 $RDF::Trine::Store::STORE_CLASSES{ $class } = $VERSION;
45 68         158886 @pos_names = qw(subject predicate object context);
46             }
47              
48             ######################################################################
49              
50             =head1 METHODS
51              
52             Beyond the methods documented below, this class inherits methods from the
53             L<RDF::Trine::Store> class.
54              
55             =over 4
56              
57             =item C<< new () >>
58              
59             Returns a new memory-backed storage object.
60              
61             =item C<new_with_config ( $hashref )>
62              
63             Returns a new storage object configured with a hashref with certain
64             keys as arguments.
65              
66             The C<storetype> key must be C<Memory> for this backend.
67              
68             This module also supports initializing the store from a file or URL,
69             in which case, a C<sources> key may be used. This holds an arrayref of
70             hashrefs. To load a file, you may give the file name with a C<file>
71             key in the hashref, and to load a URL, use C<url>. See example
72             below. Furthermore, the following keys may be used:
73              
74             =over
75              
76             =item C<syntax>
77              
78             The syntax of the parsed file or URL.
79              
80             =item C<base_uri>
81              
82             The base URI to be used for a parsed file.
83              
84             =item C<graph> NOT IMPLEMENTED
85              
86             Use this URI as a graph name for the contents of the file or URL.
87              
88             =back
89              
90             The following example initializes a Memory store based on a local file and a remote URL:
91              
92             my $store = RDF::Trine::Store->new_with_config(
93             {
94             storetype => 'Memory',
95             sources => [
96             {
97             file => 'test-23.ttl',
98             syntax => 'turtle',
99             },
100             {
101             url => 'http://www.kjetil.kjernsmo.net/foaf',
102             syntax => 'rdfxml',
103             graph => 'http://example.org/graph/remote-users'
104             }
105             ]
106             });
107              
108             =cut
109              
110             sub new {
111 930     930 1 1879 my $class = shift;
112 930         6520 my $self = bless({
113             size => 0,
114             statements => [],
115             subject => {},
116             predicate => {},
117             object => {},
118             context => {},
119             ctx_nodes => {},
120             hash => Digest::SHA->new,
121             statement_hashes => {},
122             }, $class);
123              
124 930         18801 return $self;
125             }
126              
127             sub _new_with_string {
128 3     3   9 my $class = shift;
129 3   50     19 my $config = shift || '';
130 3         13 my @uris = split(';', $config);
131 3         10 my $self = $class->new();
132            
133 3         22 my $model = RDF::Trine::Model->new( $self );
134 3         9 foreach my $u (@uris) {
135 0         0 RDF::Trine::Parser->parse_url_into_model( $u, $model );
136             }
137            
138 3         33 return $self;
139             }
140              
141             sub _config_meta {
142             return {
143 0     0   0 required_keys => []
144             }
145             }
146              
147             sub _new_with_config {
148 3     3   7 my $class = shift;
149 3         5 my $config = shift;
150 3         5 my @sources = @{$config->{sources}};
  3         9  
151 3         9 my $self = $class->new();
152 3         6 foreach my $source (@sources) {
153 4         28 my %args;
154 4 100       15 if (my $g = $source->{graph}) {
155 1 50       8 $args{context} = (blessed($g) ? $g : iri($g));
156             }
157 4 50       12 if ($source->{url}) {
    50          
158 0         0 my $parser = RDF::Trine::Parser->new($source->{syntax});
159 0         0 my $model = RDF::Trine::Model->new( $self );
160 0         0 $parser->parse_url_into_model( $source->{url}, $model, %args );
161            
162             } elsif ($source->{file}) {
163             open(my $fh, "<:encoding(UTF-8)", $source->{file})
164 4 50   1   133 || throw RDF::Trine::Error -text => "Couldn't open file $source->{file}";
  1         6  
  1         2  
  1         5  
165 4         1169 my $parser = RDF::Trine::Parser->new($source->{syntax});
166 4         18 my $model = RDF::Trine::Model->new( $self );
167 4         22 $parser->parse_file_into_model( $source->{base_uri}, $source->{file}, $model, %args );
168             } else {
169 0         0 throw RDF::Trine::Error::MethodInvocationError -text => "$class needs a url or file argument";
170             }
171             }
172 2         28 return $self;
173             }
174              
175              
176              
177             =item C<< temporary_store >>
178              
179             Returns a temporary (empty) triple store.
180              
181             =cut
182              
183             sub temporary_store {
184 3     3 1 33 my $class = shift;
185 3         13 return $class->new();
186             }
187              
188             =item C<< get_statements ( $subject, $predicate, $object [, $context] ) >>
189              
190             Returns a stream object of all statements matching the specified subject,
191             predicate and objects. Any of the arguments may be undef to match any value.
192              
193             =cut
194              
195             sub get_statements {
196 3196     3196 1 5546 my $self = shift;
197 3196         8354 my @nodes = @_[0..3];
198 3196         5341 my $bound = 0;
199 3196         4936 my %bound;
200            
201 3196         4801 my $use_quad = 0;
202 3196 100       7677 if (scalar(@_) >= 4) {
203 2195         3533 $use_quad = 1;
204 2195         3255 my $g = $nodes[3];
205 2195 100 100     8782 if (blessed($g) and not($g->is_variable)) {
206 65         115 $bound++;
207 65         132 $bound{ 3 } = $g;
208             }
209             }
210            
211 3196         7027 foreach my $pos (0 .. 2) {
212 9588         15816 my $n = $nodes[ $pos ];
213 9588 100 100     35596 if (blessed($n) and not($n->is_variable)) {
214 4248         6972 $bound++;
215 4248         10482 $bound{ $pos } = $n;
216             }
217             }
218            
219 3196 100       11989 my $iter = ($use_quad)
220             ? $self->_get_statements_quad( $bound, %bound )
221             : $self->_get_statements_triple( $bound, %bound );
222 3196         125737 return $iter;
223             }
224              
225             sub _get_statements_triple {
226 1001     1001   1659 my $self = shift;
227 1001         1456 my $bound = shift;
228 1001         2458 my %bound = @_;
229            
230 1001         1931 my $match_set = Set::Scalar->new( 0 .. $#{ $self->{statements} } );
  1001         4082  
231 1001 100       103728 if ($bound) {
232             # warn "getting $bound-bound statements";
233 889         2940 my @pos = sort { $a <=> $b } keys %bound;
  622         2440  
234 889         2333 my @names = @pos_names[ @pos ];
235             # warn "\tbound nodes are: " . join(', ', @names) . "\n";
236            
237 889         1393 my @sets;
238 889         2016 foreach my $i (0 .. $#pos) {
239 1508         2696 my $pos = $pos[ $i ];
240 1508         2612 my $node = $bound{ $pos };
241 1508         4488 my $string = $node->as_string;
242             # warn $node . " has string: '" . $string . "'\n";
243 1508         3234 my $hash = $self->{$names[$i]};
244 1508         2976 my $set = $hash->{ $string };
245 1508         3072 push(@sets, $set);
246             }
247            
248 889         1752 foreach my $s (@sets) {
249 1470 100       4928 unless (blessed($s)) {
250 275         964 return RDF::Trine::Iterator::Graph->new();
251             }
252             }
253            
254             # warn "initial set: $i\n";
255 614         1521 while (@sets) {
256 1021         192721 my $s = shift(@sets);
257             # warn "new set: $s\n";
258 1021         3018 $match_set = $match_set->intersection($s);
259             # warn "intersection: $i";
260             }
261             }
262            
263 726         285886 my $open = 1;
264 726         1278 my %seen;
265            
266 726         2019 my @members = sort { $a <=> $b } $match_set->members;
  5743         9085  
267             my $sub = sub {
268 1903     1903   2902 while (1) {
269 2660         4237 my $e = shift(@members);
270 2660 100       5973 unless (defined($e)) {
271 713         1133 $open = 0;
272 713         1607 return;
273             }
274            
275 1947         3649 my $st = $self->{statements}[ $e++ ];
276 1947 100       5981 unless (blessed($st)) {
277 333         464 next;
278             }
279 1614         4646 my @nodes = $st->nodes;
280 1614         5183 my $triple = RDF::Trine::Statement->new( @nodes[0..2] );
281 1614 100       4217 if ($seen{ $triple->as_string }++) {
282             # warn "already seen " . $triple->as_string . "\n" if ($::debug);
283 424         867 next;
284             }
285             # warn "returning statement from $bound-bound iterator: " . $triple->as_string . "\n";
286 1190         3582 return $triple;
287             }
288 726         6477 };
289 726         3194 return RDF::Trine::Iterator::Graph->new( $sub );
290             }
291              
292             sub _get_statements_quad {
293 2195     2195   3496 my $self = shift;
294 2195         3396 my $bound = shift;
295 2195         5134 my %bound = @_;
296 2195 100       5380 if ($bound == 0) {
297             # warn "getting all statements";
298             # warn Dumper($self);
299 792         1355 my $i = 0;
300             my $sub = sub {
301             # warn "quad iter called with i=$i, last=" . $#{ $self->{statements} };
302 2373 100   2373   3606 return unless ($i <= $#{ $self->{statements} });
  2373         7176  
303 1581         2946 my $st = $self->{statements}[ $i ];
304             # warn $st;
305 1581   66     6386 while (not(blessed($st)) and ($i <= $#{ $self->{statements} })) {
  16         47  
306 16         41 $st = $self->{statements}[ ++$i ];
307             # warn "null st. next: $st";
308             }
309 1581         2680 $i++;
310 1581         3421 return $st;
311 792         3568 };
312             # warn "returning all quads sub $sub";
313 792         4740 return RDF::Trine::Iterator::Graph->new( $sub );
314             }
315            
316 1403         2223 my $match_set;
317 1403 100       3119 if ($bound == 1) {
318             # warn "getting 1-bound statements";
319 50         112 my ($pos) = keys %bound;
320 50         152 my $name = $pos_names[ $pos ];
321             # warn "\tbound node is $name\n";
322 50         89 my $node = $bound{ $pos };
323 50         157 my $string = $node->as_string;
324 50         136 $match_set = $self->{$name}{ $string };
325             # warn "\tmatching statements: $match_set\n";
326 50 100       193 unless (blessed($match_set)) {
327 1         6 return RDF::Trine::Iterator::Graph->new();
328             }
329             } else {
330             # warn "getting $bound-bound statements";
331 1353         3534 my @pos = keys %bound;
332 1353         4733 my @names = @pos_names[ @pos ];
333             # warn "\tbound nodes are: " . join(', ', @names) . "\n";
334            
335 1353         2339 my @sets;
336 1353         2984 foreach my $i (0 .. $#pos) {
337 2755         4562 my $pos = $pos[ $i ];
338 2755         4511 my $node = $bound{ $pos };
339 2755         8086 my $string = $node->as_string;
340             # warn $node . " has string: '" . $string . "'\n";
341 2755         6142 my $hash = $self->{$names[$i]};
342 2755         7211 my $set = $hash->{ $string };
343 2755         6637 push(@sets, $set);
344             }
345            
346 1353         2750 foreach my $s (@sets) {
347 2751 100       9157 unless (blessed($s)) {
348 2         11 return RDF::Trine::Iterator::Graph->new();
349             }
350             }
351 1351         2429 my $i = shift(@sets);
352             # warn "initial set: $i\n";
353 1351         3552 while (@sets) {
354 1398         19627 my $s = shift(@sets);
355             # warn "new set: $s\n";
356 1398         4710 $i = $i->intersection($s);
357             # warn "intersection: $i";
358             }
359 1351         3860230 $match_set = $i;
360             # warn "\tmatching statements: $match_set\n";
361             }
362            
363 1400         2600 my $open = 1;
364 1400         4102 my @e = $match_set->elements;
365             my $sub = sub {
366 3417 100   3417   8222 unless (scalar(@e)) {
367 1395         2653 $open = 0;
368 1395         3271 return;
369             }
370 2022         3735 my $e = shift(@e);
371             # warn "quad iterator returning statement $e";
372            
373 2022         4560 my $st = $self->{statements}[ $e ];
374             # warn "returning statement from $bound-bound iterator: " . $st->as_string . "\n";
375 2022         4057 return $st;
376 1400         17069 };
377 1400         6792 return RDF::Trine::Iterator::Graph->new( $sub );
378             }
379              
380             =item C<< get_contexts >>
381              
382             Returns an RDF::Trine::Iterator over the RDF::Trine::Node objects comprising
383             the set of contexts of the stored quads.
384              
385             =cut
386              
387             sub get_contexts {
388 7     7 1 24 my $self = shift;
389 7         12 my @ctx = grep { not($_->isa('RDF::Trine::Node::Nil')) } values %{ $self->{ ctx_nodes } };
  20         114  
  7         24  
390 7         42 return RDF::Trine::Iterator->new( \@ctx );
391             }
392              
393             =item C<< add_statement ( $statement [, $context] ) >>
394              
395             Adds the specified C<$statement> to the underlying model.
396              
397             =cut
398              
399             sub add_statement {
400 4187     4187 1 8458 my $self = shift;
401 4187         6347 my $st = shift;
402 4187         6513 my $context = shift;
403            
404 4187 100       16893 if ($st->isa( 'RDF::Trine::Statement::Quad' )) {
405 142 100       416 if (blessed($context)) {
406 1         16 throw RDF::Trine::Error::MethodInvocationError -text => "add_statement cannot be called with both a quad and a context";
407             }
408             } else {
409 4045         10899 my @nodes = $st->nodes;
410 4045 100       11269 if (blessed($context)) {
411 13         42 $st = RDF::Trine::Statement::Quad->new( @nodes[0..2], $context );
412             } else {
413 4032         15492 my $nil = RDF::Trine::Node::Nil->new();
414 4032         15131 $st = RDF::Trine::Statement::Quad->new( @nodes[0..2], $nil );
415             }
416             }
417            
418 4186         13672 my $string = encode_utf8($st->as_string);
419 4186         59421 my $st_hash = sha1($string);
420 4186 100       20764 if ($self->{statement_hashes}{$st_hash}++) {
421 11         36 my $count = $self->count_statements( $st->nodes );
422             # warn "store already has statement " . $st->as_string;
423 11 100       319 return if $count;
424             }
425            
426 4179         8375 $self->{size}++;
427 4179         6001 my $id = scalar(@{ $self->{ statements } });
  4179         8464  
428 4179         24279 $self->{hash}->add('+' . $string);
429 4179         7530 push( @{ $self->{ statements } }, $st );
  4179         9687  
430 4179         11212 foreach my $pos (0 .. $#pos_names) {
431 16716         642143 my $name = $pos_names[ $pos ];
432 16716         55297 my $node = $st->$name();
433 16716         49315 my $string = $node->as_string;
434 16716         41857 my $set = $self->{$name}{ $string };
435 16716 100       50044 unless (blessed($set)) {
436 7680         24278 $set = Set::Scalar->new();
437 7680         445054 $self->{$name}{ $string } = $set;
438             }
439 16716         43782 $set->insert( $id );
440             }
441            
442 4179         201911 my $ctx = $st->context;
443 4179         10783 my $str = $ctx->as_string;
444 4179 100       12030 unless (exists $self->{ ctx_nodes }{ $str }) {
445 953         2199 $self->{ ctx_nodes }{ $str } = $ctx;
446             }
447 4179         19181 return;
448             }
449              
450             =item C<< remove_statement ( $statement [, $context]) >>
451              
452             Removes the specified C<$statement> from the underlying model.
453              
454             =cut
455              
456             sub remove_statement {
457 164     164 1 282 my $self = shift;
458 164         272 my $st = shift;
459 164         260 my $context = shift;
460            
461 164 100       660 if ($st->isa( 'RDF::Trine::Statement::Quad' )) {
462 109 100       362 if (blessed($context)) {
463 1         5 throw RDF::Trine::Error::MethodInvocationError -text => "remove_statement cannot be called with both a quad and a context";
464             }
465             } else {
466 55         243 my @nodes = $st->nodes;
467 55 100       214 if (blessed($context)) {
468 4         16 $st = RDF::Trine::Statement::Quad->new( @nodes[0..2], $context );
469             } else {
470 51         212 my $nil = RDF::Trine::Node::Nil->new();
471 51         239 $st = RDF::Trine::Statement::Quad->new( @nodes[0..2], $nil );
472             }
473             }
474              
475 163         498 my $string = encode_utf8($st->as_string);
476 163         2066 my $st_hash = sha1($string);
477 163 100       575 unless (exists $self->{statement_hashes}{$st_hash}) {
478 11         55 return;
479             }
480            
481 152 100       457 if (0 == --$self->{statement_hashes}{$st_hash}) {
482 144         348 delete $self->{statement_hashes}{$st_hash};
483             }
484            
485 152         439 my @nodes = $st->nodes;
486 152         519 my $count = $self->count_statements( @nodes[ 0..3 ] );
487             # warn "remove_statement: count of statement is $count";
488 152 50       6341 if ($count > 0) {
489 152         325 $self->{size}--;
490 152         522 my $id = $self->_statement_id( $st->nodes );
491             # warn "removing statement $id: " . $st->as_string . "\n";
492 152         6311 $self->{hash}->add('-' . $string);
493 152         386 $self->{statements}[ $id ] = undef;
494 152         405 foreach my $pos (0 .. 3) {
495 608         4954 my $name = $pos_names[ $pos ];
496 608         1888 my $node = $st->$name();
497 608         1641 my $str = $node->as_string;
498 608         1322 my $set = $self->{$name}{ $str };
499 608         1597 $set->delete( $id );
500 608 100       13670 if ($set->size == 0) {
501 92 100       638 if ($pos == 3) {
502 18         48 delete $self->{ ctx_nodes }{ $str };
503             }
504 92         292 delete $self->{$name}{ $str };
505             }
506             }
507             }
508 152         1909 return;
509             }
510              
511             =item C<< remove_statements ( $subject, $predicate, $object [, $context]) >>
512              
513             Removes the specified C<$statement> from the underlying model.
514              
515             =cut
516              
517             sub remove_statements {
518 8     8 1 18 my $self = shift;
519 8         17 my $subj = shift;
520 8         16 my $pred = shift;
521 8         26 my $obj = shift;
522 8         16 my $context = shift;
523 8         28 my $iter = $self->get_statements( $subj, $pred, $obj, $context );
524 8         29 while (my $st = $iter->next) {
525 19         54 $self->remove_statement( $st );
526             }
527             }
528              
529             =item C<< count_statements ( $subject, $predicate, $object, $context ) >>
530              
531             Returns a count of all the statements matching the specified subject,
532             predicate, object, and context. Any of the arguments may be undef to match any
533             value.
534              
535             =cut
536              
537             sub count_statements {
538 906     906 1 1474 my $self = shift;
539 906         2382 my @nodes = @_[0..3];
540 906         1437 my $bound = 0;
541 906         1399 my %bound;
542            
543 906         1395 my $use_quad = 0;
544 906 100       2151 if (scalar(@_) >= 4) {
545 261         429 $use_quad = 1;
546             # warn "count statements with quad" if ($::debug);
547 261         415 my $g = $nodes[3];
548 261 100 100     1407 if (blessed($g) and not($g->is_variable)) {
549 197         355 $bound++;
550 197         479 $bound{ 3 } = $g;
551             }
552             }
553            
554 906         2023 foreach my $pos (0 .. 2) {
555 2718         4203 my $n = $nodes[ $pos ];
556             # unless (blessed($n)) {
557             # $n = RDF::Trine::Node::Nil->new();
558             # $nodes[ $pos ] = $n;
559             # }
560            
561 2718 100 100     9853 if (blessed($n) and not($n->is_variable)) {
562 1628         2561 $bound++;
563 1628         4221 $bound{ $pos } = $n;
564             }
565             }
566            
567             # warn "use quad: $use_quad\n" if ($::debug);
568             # warn "bound: $bound\n" if ($::debug);
569 906 100       2067 if ($use_quad) {
570 261 100       752 if ($bound == 0) {
    100          
571             # warn "counting all statements";
572 35         113 return $self->size;
573             } elsif ($bound == 1) {
574 34         89 my ($pos) = keys %bound;
575 34         87 my $name = $pos_names[ $pos ];
576 34         125 my $set = $self->{$name}{ $bound{ $pos }->as_string };
577             # warn Dumper($set) if ($::debug);
578 34 100       134 unless (blessed($set)) {
579 4         23 return 0;
580             }
581 30         116 return $set->size;
582             } else {
583 192         603 my @pos = keys %bound;
584 192         705 my @names = @pos_names[ @pos ];
585 192         322 my @sets;
586 192         472 foreach my $i (0 .. $#names) {
587 711         1117 my $pos = $pos[ $i ];
588 711         1038 my $setname = $names[ $i ];
589 711         1159 my $data = $self->{ $setname };
590            
591 711         1056 my $node = $bound{ $pos };
592 711         1760 my $str = $node->as_string;
593 711         1370 my $set = $data->{ $str };
594 711         1392 push( @sets, $set );
595             }
596 192         361 foreach my $s (@sets) {
597             # warn "set: " . Dumper($s) if ($::debug);
598 701 100       2058 unless (blessed($s)) {
599             # warn "*** returning zero" if ($::debug);
600 5         21 return 0;
601             }
602             }
603 187         320 my $i = shift(@sets);
604 187         470 while (@sets) {
605 506         172866 my $s = shift(@sets);
606 506         1520 $i = $i->intersection($s);
607             }
608 187         85652 return $i->size;
609             }
610             } else {
611             # use_quad is false here
612             # we're counting distinct (s,p,o) triples from the quadstore
613 645         957 my $count = 0;
614 645         1661 my $iter = $self->get_statements( @nodes[ 0..2 ] );
615 645         2005 while (my $st = $iter->next) {
616             # warn $st->as_string if ($::debug);
617 525         1427 $count++;
618             }
619 645         5623 return $count;
620             }
621             }
622              
623             =item C<< etag >>
624              
625             If the store has the capability and knowledge to support caching, returns a
626             persistent token that will remain consistent as long as the store's data doesn't
627             change. This token is acceptable for use as an HTTP ETag.
628              
629             =cut
630              
631             sub etag {
632 7     7 1 11 my $self = shift;
633 7         58 return $self->{hash}->b64digest;
634             }
635              
636             =item C<< size >>
637              
638             Returns the number of statements in the store.
639              
640             =cut
641              
642             sub size {
643 72     72 1 1182 my $self = shift;
644 72         148 my $size = $self->{size};
645 72         404 return $size;
646             }
647              
648             =item C<< supports ( [ $feature ] ) >>
649              
650             If C<< $feature >> is specified, returns true if the feature is supported by the
651             store, false otherwise. If C<< $feature >> is not specified, returns a list of
652             supported features.
653              
654             =cut
655              
656             sub supports {
657 0     0 1 0 return;
658             }
659              
660             sub _statement_id {
661 154     154   281 my $self = shift;
662 154         368 my @nodes = @_;
663 154         359 foreach my $pos (0 .. 3) {
664 616         973 my $n = $nodes[ $pos ];
665             # unless (blessed($n)) {
666             # $n = RDF::Trine::Node::Nil->new();
667             # $nodes[ $pos ] = $n;
668             # }
669             }
670            
671 154         330 my ($subj, $pred, $obj, $context) = @nodes;
672            
673 154         324 my @pos = (0 .. 3);
674 154         392 my @names = @pos_names[ @pos ];
675 154         241 my @sets;
676 154         323 foreach my $i (0 .. $#names) {
677 616         898 my $pos = $pos[ $i ];
678 616         886 my $setname = $names[ $i ];
679 616         1030 my $data = $self->{ $setname };
680 616         864 my $node = $nodes[ $pos ];
681 616         1565 my $str = $node->as_string;
682 616         1151 my $set = $data->{ $str };
683 616         1181 push( @sets, $set );
684             }
685            
686 154         292 foreach my $s (@sets) {
687 613 100       1789 unless (blessed($s)) {
688 1         6 return -1;
689             }
690             }
691 153         289 my $i = shift(@sets);
692 153         382 while (@sets) {
693 459         166148 my $s = shift(@sets);
694 459         1241 $i = $i->intersection($s);
695             }
696 153 100       70070 if ($i->size == 1) {
697 152         1106 my ($id) = $i->members;
698 152         1039 return $id;
699             } else {
700 1         9 return -1;
701             }
702             }
703              
704             # sub _debug {
705             # my $self = shift;
706             # my $size = scalar(@{ $self->{statements} });
707             # warn "Memory quad-store contains " . $size . " statements:\n";
708             # foreach my $st (@{ $self->{statements} }) {
709             # if (blessed($st)) {
710             # warn $st->as_string . "\n";
711             # }
712             # }
713             # }
714              
715             1;
716              
717             __END__
718              
719             =back
720              
721             =head1 BUGS
722              
723             Please report any bugs or feature requests to through the GitHub web interface
724             at L<https://github.com/kasei/perlrdf/issues>.
725              
726             =head1 AUTHOR
727              
728             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
729              
730             =head1 COPYRIGHT
731              
732             Copyright (c) 2006-2012 Gregory Todd Williams. This
733             program is free software; you can redistribute it and/or modify it under
734             the same terms as Perl itself.
735              
736             =cut