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.017
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   407 use strict;
  68         154  
  68         1658  
22 68     68   310 use warnings;
  68         141  
  68         1407  
23 68     68   312 no warnings 'redefine';
  68         140  
  68         1698  
24 68     68   328 use base qw(RDF::Trine::Store);
  68         141  
  68         4269  
25              
26 68     68   405 use Encode;
  68         152  
  68         4515  
27 68     68   27013 use Set::Scalar;
  68         568019  
  68         2653  
28 68     68   527 use Data::Dumper;
  68         162  
  68         2807  
29 68     68   30662 use Digest::SHA qw(sha1);
  68         176947  
  68         4917  
30 68     68   537 use List::Util qw(first);
  68         159  
  68         5559  
31 68     68   448 use Scalar::Util qw(refaddr reftype blessed);
  68         142  
  68         2998  
32 68     68   24566 use RDF::Trine::Statement::Quad;
  68         234  
  68         2318  
33              
34 68     68   463 use RDF::Trine qw(iri);
  68         154  
  68         2944  
35 68     68   401 use RDF::Trine::Error;
  68         148  
  68         506  
36              
37             ######################################################################
38              
39             my @pos_names;
40             our $VERSION;
41             BEGIN {
42 68     68   5830 $VERSION = "1.017";
43 68         160 my $class = __PACKAGE__;
44 68         204 $RDF::Trine::Store::STORE_CLASSES{ $class } = $VERSION;
45 68         164328 @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 2322 my $class = shift;
112 930         9139 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         22921 return $self;
125             }
126              
127             sub _new_with_string {
128 3     3   10 my $class = shift;
129 3   50     18 my $config = shift || '';
130 3         12 my @uris = split(';', $config);
131 3         11 my $self = $class->new();
132            
133 3         24 my $model = RDF::Trine::Model->new( $self );
134 3         11 foreach my $u (@uris) {
135 0         0 RDF::Trine::Parser->parse_url_into_model( $u, $model );
136             }
137            
138 3         34 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   6 my $class = shift;
149 3         9 my $config = shift;
150 3         6 my @sources = @{$config->{sources}};
  3         10  
151 3         14 my $self = $class->new();
152 3         8 foreach my $source (@sources) {
153 4         61 my %args;
154 4 100       31 if (my $g = $source->{graph}) {
155 1 50       10 $args{context} = (blessed($g) ? $g : iri($g));
156             }
157 4 50       16 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   212 || throw RDF::Trine::Error -text => "Couldn't open file $source->{file}";
  1         6  
  1         2  
  1         8  
165 4         1383 my $parser = RDF::Trine::Parser->new($source->{syntax});
166 4         28 my $model = RDF::Trine::Model->new( $self );
167 4         28 $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         35 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 43 my $class = shift;
185 3         14 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 5972 my $self = shift;
197 3196         8956 my @nodes = @_[0..3];
198 3196         5475 my $bound = 0;
199 3196         5410 my %bound;
200            
201 3196         5176 my $use_quad = 0;
202 3196 100       8452 if (scalar(@_) >= 4) {
203 2195         3763 $use_quad = 1;
204 2195         3588 my $g = $nodes[3];
205 2195 100 100     10236 if (blessed($g) and not($g->is_variable)) {
206 65         119 $bound++;
207 65         141 $bound{ 3 } = $g;
208             }
209             }
210            
211 3196         7272 foreach my $pos (0 .. 2) {
212 9588         16577 my $n = $nodes[ $pos ];
213 9588 100 100     37890 if (blessed($n) and not($n->is_variable)) {
214 4248         7297 $bound++;
215 4248         12039 $bound{ $pos } = $n;
216             }
217             }
218            
219 3196 100       13128 my $iter = ($use_quad)
220             ? $self->_get_statements_quad( $bound, %bound )
221             : $self->_get_statements_triple( $bound, %bound );
222 3196         131416 return $iter;
223             }
224              
225             sub _get_statements_triple {
226 1001     1001   1792 my $self = shift;
227 1001         1582 my $bound = shift;
228 1001         2530 my %bound = @_;
229            
230 1001         1874 my $match_set = Set::Scalar->new( 0 .. $#{ $self->{statements} } );
  1001         4341  
231 1001 100       106240 if ($bound) {
232             # warn "getting $bound-bound statements";
233 889         3452 my @pos = sort { $a <=> $b } keys %bound;
  623         2599  
234 889         2480 my @names = @pos_names[ @pos ];
235             # warn "\tbound nodes are: " . join(', ', @names) . "\n";
236            
237 889         1395 my @sets;
238 889         2176 foreach my $i (0 .. $#pos) {
239 1508         2593 my $pos = $pos[ $i ];
240 1508         2511 my $node = $bound{ $pos };
241 1508         4674 my $string = $node->as_string;
242             # warn $node . " has string: '" . $string . "'\n";
243 1508         3501 my $hash = $self->{$names[$i]};
244 1508         3103 my $set = $hash->{ $string };
245 1508         3426 push(@sets, $set);
246             }
247            
248 889         1714 foreach my $s (@sets) {
249 1470 100       5090 unless (blessed($s)) {
250 275         995 return RDF::Trine::Iterator::Graph->new();
251             }
252             }
253            
254             # warn "initial set: $i\n";
255 614         1647 while (@sets) {
256 1021         193221 my $s = shift(@sets);
257             # warn "new set: $s\n";
258 1021         3078 $match_set = $match_set->intersection($s);
259             # warn "intersection: $i";
260             }
261             }
262            
263 726         299488 my $open = 1;
264 726         1345 my %seen;
265            
266 726         2160 my @members = sort { $a <=> $b } $match_set->members;
  5686         9385  
267             my $sub = sub {
268 1903     1903   3080 while (1) {
269 2660         4358 my $e = shift(@members);
270 2660 100       6195 unless (defined($e)) {
271 713         1231 $open = 0;
272 713         1658 return;
273             }
274            
275 1947         3947 my $st = $self->{statements}[ $e++ ];
276 1947 100       6424 unless (blessed($st)) {
277 333         479 next;
278             }
279 1614         4817 my @nodes = $st->nodes;
280 1614         5410 my $triple = RDF::Trine::Statement->new( @nodes[0..2] );
281 1614 100       4399 if ($seen{ $triple->as_string }++) {
282             # warn "already seen " . $triple->as_string . "\n" if ($::debug);
283 424         956 next;
284             }
285             # warn "returning statement from $bound-bound iterator: " . $triple->as_string . "\n";
286 1190         3901 return $triple;
287             }
288 726         6719 };
289 726         3274 return RDF::Trine::Iterator::Graph->new( $sub );
290             }
291              
292             sub _get_statements_quad {
293 2195     2195   3948 my $self = shift;
294 2195         3965 my $bound = shift;
295 2195         6123 my %bound = @_;
296 2195 100       5743 if ($bound == 0) {
297             # warn "getting all statements";
298             # warn Dumper($self);
299 792         1459 my $i = 0;
300             my $sub = sub {
301             # warn "quad iter called with i=$i, last=" . $#{ $self->{statements} };
302 2373 100   2373   3790 return unless ($i <= $#{ $self->{statements} });
  2373         7918  
303 1581         3688 my $st = $self->{statements}[ $i ];
304             # warn $st;
305 1581   66     7482 while (not(blessed($st)) and ($i <= $#{ $self->{statements} })) {
  16         47  
306 16         45 $st = $self->{statements}[ ++$i ];
307             # warn "null st. next: $st";
308             }
309 1581         3093 $i++;
310 1581         3707 return $st;
311 792         4428 };
312             # warn "returning all quads sub $sub";
313 792         5502 return RDF::Trine::Iterator::Graph->new( $sub );
314             }
315            
316 1403         2322 my $match_set;
317 1403 100       3158 if ($bound == 1) {
318             # warn "getting 1-bound statements";
319 50         126 my ($pos) = keys %bound;
320 50         144 my $name = $pos_names[ $pos ];
321             # warn "\tbound node is $name\n";
322 50         112 my $node = $bound{ $pos };
323 50         191 my $string = $node->as_string;
324 50         181 $match_set = $self->{$name}{ $string };
325             # warn "\tmatching statements: $match_set\n";
326 50 100       225 unless (blessed($match_set)) {
327 1         7 return RDF::Trine::Iterator::Graph->new();
328             }
329             } else {
330             # warn "getting $bound-bound statements";
331 1353         3759 my @pos = keys %bound;
332 1353         5017 my @names = @pos_names[ @pos ];
333             # warn "\tbound nodes are: " . join(', ', @names) . "\n";
334            
335 1353         2550 my @sets;
336 1353         3127 foreach my $i (0 .. $#pos) {
337 2755         4807 my $pos = $pos[ $i ];
338 2755         4505 my $node = $bound{ $pos };
339 2755         8288 my $string = $node->as_string;
340             # warn $node . " has string: '" . $string . "'\n";
341 2755         6610 my $hash = $self->{$names[$i]};
342 2755         7889 my $set = $hash->{ $string };
343 2755         6087 push(@sets, $set);
344             }
345            
346 1353         2849 foreach my $s (@sets) {
347 2755 100       9799 unless (blessed($s)) {
348 2         11 return RDF::Trine::Iterator::Graph->new();
349             }
350             }
351 1351         2583 my $i = shift(@sets);
352             # warn "initial set: $i\n";
353 1351         3469 while (@sets) {
354 1398         18631 my $s = shift(@sets);
355             # warn "new set: $s\n";
356 1398         5148 $i = $i->intersection($s);
357             # warn "intersection: $i";
358             }
359 1351         3996163 $match_set = $i;
360             # warn "\tmatching statements: $match_set\n";
361             }
362            
363 1400         2819 my $open = 1;
364 1400         4217 my @e = $match_set->elements;
365             my $sub = sub {
366 3417 100   3417   8294 unless (scalar(@e)) {
367 1395         2554 $open = 0;
368 1395         3514 return;
369             }
370 2022         3643 my $e = shift(@e);
371             # warn "quad iterator returning statement $e";
372            
373 2022         4668 my $st = $self->{statements}[ $e ];
374             # warn "returning statement from $bound-bound iterator: " . $st->as_string . "\n";
375 2022         4263 return $st;
376 1400         17046 };
377 1400         7425 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 28 my $self = shift;
389 7         15 my @ctx = grep { not($_->isa('RDF::Trine::Node::Nil')) } values %{ $self->{ ctx_nodes } };
  20         101  
  7         31  
390 7         49 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 8731 my $self = shift;
401 4187         6780 my $st = shift;
402 4187         6873 my $context = shift;
403            
404 4187 100       18841 if ($st->isa( 'RDF::Trine::Statement::Quad' )) {
405 142 100       428 if (blessed($context)) {
406 1         19 throw RDF::Trine::Error::MethodInvocationError -text => "add_statement cannot be called with both a quad and a context";
407             }
408             } else {
409 4045         10619 my @nodes = $st->nodes;
410 4045 100       12082 if (blessed($context)) {
411 13         49 $st = RDF::Trine::Statement::Quad->new( @nodes[0..2], $context );
412             } else {
413 4032         16070 my $nil = RDF::Trine::Node::Nil->new();
414 4032         17133 $st = RDF::Trine::Statement::Quad->new( @nodes[0..2], $nil );
415             }
416             }
417            
418 4186         14671 my $string = encode_utf8($st->as_string);
419 4186         61230 my $st_hash = sha1($string);
420 4186 100       21445 if ($self->{statement_hashes}{$st_hash}++) {
421 11         39 my $count = $self->count_statements( $st->nodes );
422             # warn "store already has statement " . $st->as_string;
423 11 100       376 return if $count;
424             }
425            
426 4179         8907 $self->{size}++;
427 4179         7292 my $id = scalar(@{ $self->{ statements } });
  4179         9061  
428 4179         26728 $self->{hash}->add('+' . $string);
429 4179         7905 push( @{ $self->{ statements } }, $st );
  4179         10428  
430 4179         12594 foreach my $pos (0 .. $#pos_names) {
431 16716         657547 my $name = $pos_names[ $pos ];
432 16716         58822 my $node = $st->$name();
433 16716         52927 my $string = $node->as_string;
434 16716         42278 my $set = $self->{$name}{ $string };
435 16716 100       51324 unless (blessed($set)) {
436 7680         26492 $set = Set::Scalar->new();
437 7680         456638 $self->{$name}{ $string } = $set;
438             }
439 16716         47035 $set->insert( $id );
440             }
441            
442 4179         208523 my $ctx = $st->context;
443 4179         11455 my $str = $ctx->as_string;
444 4179 100       12806 unless (exists $self->{ ctx_nodes }{ $str }) {
445 953         2503 $self->{ ctx_nodes }{ $str } = $ctx;
446             }
447 4179         20061 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 296 my $self = shift;
458 164         246 my $st = shift;
459 164         282 my $context = shift;
460            
461 164 100       668 if ($st->isa( 'RDF::Trine::Statement::Quad' )) {
462 109 100       368 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         165 my @nodes = $st->nodes;
467 55 100       186 if (blessed($context)) {
468 4         21 $st = RDF::Trine::Statement::Quad->new( @nodes[0..2], $context );
469             } else {
470 51         174 my $nil = RDF::Trine::Node::Nil->new();
471 51         189 $st = RDF::Trine::Statement::Quad->new( @nodes[0..2], $nil );
472             }
473             }
474              
475 163         495 my $string = encode_utf8($st->as_string);
476 163         2097 my $st_hash = sha1($string);
477 163 100       584 unless (exists $self->{statement_hashes}{$st_hash}) {
478 11         47 return;
479             }
480            
481 152 100       443 if (0 == --$self->{statement_hashes}{$st_hash}) {
482 144         348 delete $self->{statement_hashes}{$st_hash};
483             }
484            
485 152         447 my @nodes = $st->nodes;
486 152         518 my $count = $self->count_statements( @nodes[ 0..3 ] );
487             # warn "remove_statement: count of statement is $count";
488 152 50       6674 if ($count > 0) {
489 152         324 $self->{size}--;
490 152         516 my $id = $self->_statement_id( $st->nodes );
491             # warn "removing statement $id: " . $st->as_string . "\n";
492 152         6369 $self->{hash}->add('-' . $string);
493 152         374 $self->{statements}[ $id ] = undef;
494 152         429 foreach my $pos (0 .. 3) {
495 608         5098 my $name = $pos_names[ $pos ];
496 608         1888 my $node = $st->$name();
497 608         1694 my $str = $node->as_string;
498 608         1375 my $set = $self->{$name}{ $str };
499 608         1581 $set->delete( $id );
500 608 100       14394 if ($set->size == 0) {
501 92 100       675 if ($pos == 3) {
502 18         48 delete $self->{ ctx_nodes }{ $str };
503             }
504 92         301 delete $self->{$name}{ $str };
505             }
506             }
507             }
508 152         1880 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 22 my $self = shift;
519 8         16 my $subj = shift;
520 8         19 my $pred = shift;
521 8         19 my $obj = shift;
522 8         18 my $context = shift;
523 8         32 my $iter = $self->get_statements( $subj, $pred, $obj, $context );
524 8         32 while (my $st = $iter->next) {
525 19         57 $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 1621 my $self = shift;
539 906         2565 my @nodes = @_[0..3];
540 906         1604 my $bound = 0;
541 906         1515 my %bound;
542            
543 906         1414 my $use_quad = 0;
544 906 100       2219 if (scalar(@_) >= 4) {
545 261         430 $use_quad = 1;
546             # warn "count statements with quad" if ($::debug);
547 261         420 my $g = $nodes[3];
548 261 100 100     1413 if (blessed($g) and not($g->is_variable)) {
549 197         347 $bound++;
550 197         477 $bound{ 3 } = $g;
551             }
552             }
553            
554 906         2226 foreach my $pos (0 .. 2) {
555 2718         4366 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     10455 if (blessed($n) and not($n->is_variable)) {
562 1628         2854 $bound++;
563 1628         4323 $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       2103 if ($use_quad) {
570 261 100       715 if ($bound == 0) {
    100          
571             # warn "counting all statements";
572 35         113 return $self->size;
573             } elsif ($bound == 1) {
574 34         87 my ($pos) = keys %bound;
575 34         88 my $name = $pos_names[ $pos ];
576 34         127 my $set = $self->{$name}{ $bound{ $pos }->as_string };
577             # warn Dumper($set) if ($::debug);
578 34 100       138 unless (blessed($set)) {
579 4         24 return 0;
580             }
581 30         120 return $set->size;
582             } else {
583 192         593 my @pos = keys %bound;
584 192         674 my @names = @pos_names[ @pos ];
585 192         333 my @sets;
586 192         439 foreach my $i (0 .. $#names) {
587 711         1133 my $pos = $pos[ $i ];
588 711         1015 my $setname = $names[ $i ];
589 711         1169 my $data = $self->{ $setname };
590            
591 711         1076 my $node = $bound{ $pos };
592 711         1785 my $str = $node->as_string;
593 711         1496 my $set = $data->{ $str };
594 711         1444 push( @sets, $set );
595             }
596 192         360 foreach my $s (@sets) {
597             # warn "set: " . Dumper($s) if ($::debug);
598 700 100       2061 unless (blessed($s)) {
599             # warn "*** returning zero" if ($::debug);
600 5         23 return 0;
601             }
602             }
603 187         361 my $i = shift(@sets);
604 187         463 while (@sets) {
605 506         178814 my $s = shift(@sets);
606 506         1475 $i = $i->intersection($s);
607             }
608 187         90025 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         1143 my $count = 0;
614 645         1813 my $iter = $self->get_statements( @nodes[ 0..2 ] );
615 645         2137 while (my $st = $iter->next) {
616             # warn $st->as_string if ($::debug);
617 525         1450 $count++;
618             }
619 645         6089 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 17 my $self = shift;
633 7         63 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 1716 my $self = shift;
644 72         157 my $size = $self->{size};
645 72         400 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   267 my $self = shift;
662 154         423 my @nodes = @_;
663 154         373 foreach my $pos (0 .. 3) {
664 616         1028 my $n = $nodes[ $pos ];
665             # unless (blessed($n)) {
666             # $n = RDF::Trine::Node::Nil->new();
667             # $nodes[ $pos ] = $n;
668             # }
669             }
670            
671 154         336 my ($subj, $pred, $obj, $context) = @nodes;
672            
673 154         338 my @pos = (0 .. 3);
674 154         394 my @names = @pos_names[ @pos ];
675 154         227 my @sets;
676 154         340 foreach my $i (0 .. $#names) {
677 616         985 my $pos = $pos[ $i ];
678 616         913 my $setname = $names[ $i ];
679 616         957 my $data = $self->{ $setname };
680 616         895 my $node = $nodes[ $pos ];
681 616         1578 my $str = $node->as_string;
682 616         1247 my $set = $data->{ $str };
683 616         1195 push( @sets, $set );
684             }
685            
686 154         313 foreach my $s (@sets) {
687 613 100       1873 unless (blessed($s)) {
688 1         6 return -1;
689             }
690             }
691 153         269 my $i = shift(@sets);
692 153         414 while (@sets) {
693 459         172211 my $s = shift(@sets);
694 459         1230 $i = $i->intersection($s);
695             }
696 153 100       72592 if ($i->size == 1) {
697 152         1088 my ($id) = $i->members;
698 152         1096 return $id;
699             } else {
700 1         10 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