File Coverage

blib/lib/RDF/Simple/Parser/Handler.pm
Criterion Covered Total %
statement 253 294 86.0
branch 58 82 70.7
condition 28 49 57.1
subroutine 28 29 96.5
pod 1 19 5.2
total 368 473 77.8


line stmt bran cond sub pod time code
1              
2             package RDF::Simple::Parser::Handler;
3              
4 8     8   59 use strict;
  8         21  
  8         246  
5 8     8   41 use warnings;
  8         21  
  8         201  
6              
7 8     8   38 use Carp;
  8         17  
  8         468  
8 8     8   2500 use Data::Dumper; # for debugging only
  8         25115  
  8         425  
9 8     8   3202 use RDF::Simple::NS;
  8         21  
  8         245  
10 8     8   3179 use RDF::Simple::Parser::Attribs;
  8         24  
  8         315  
11 8     8   3432 use RDF::Simple::Parser::Element;
  8         26  
  8         262  
12              
13 8     8   54 use constant DEBUG => 0;
  8         103  
  8         545  
14              
15             use Class::MethodMaker [
16 8         46 scalar => [ qw/ stack base genID disallowed qnames result bnode_absolute_prefix / ],
17 8     8   54 ];
  8         62  
18              
19             our
20             $VERSION = 1.18;
21              
22             sub new
23             {
24 12     12 0 209 DEBUG && print STDERR " FFF Handler::new(@_)\n";
25 12         64 my ($class, $sink, %p) = @_;
26 12   33     84 my $self = bless {}, ref $class || $class;
27 12         313 $self->base($p{'base'});
28 12         428 $self->qnames($p{qnames});
29 12         393 $self->genID(1);
30 12         368 $self->stack([]);
31 12         92 my @dis;
32 12         41 foreach my $s (qw( RDF ID about bagID parseType resource nodeID datatype li aboutEach aboutEachPrefix ))
33             {
34 132         275 push @dis, $self->ns->uri('rdf').$s;
35             } # foreach
36 12         332 $self->disallowed(\@dis);
37 12         123 return $self;
38             } # new
39              
40             =head1 METHODS
41              
42             =over
43              
44             =cut
45              
46             sub addns
47             {
48 106     106 0 220 my ($self, $prefix, $uri) = @_;
49 106         194 DEBUG && print STDERR " DDD Handler::addns($prefix => $uri)\n";
50 106         218 $self->ns->lookup($prefix,$uri);
51             } # addns
52              
53             sub ns
54             {
55 504     504 0 789 my $self = shift;
56 504 100       1680 return $self->{_ns} if $self->{_ns};
57 12         72 $self->{_ns} = RDF::Simple::NS->new;
58             } # ns
59              
60              
61             sub _triple
62             {
63 79     79   207 my $self = shift;
64 79         158 my ($s, $p, $o) = @_;
65 79         92 if (DEBUG)
66             {
67             print STDERR " FFF $self ->_triple($s,$p,$o)\n";
68             # print STDERR Dumper(\@_);
69             my ($package, $file, $line, $sub) = caller(1);
70             print STDERR " DDD called from $sub line $line\n";
71             } # if
72 79         1947 my $r = $self->result;
73 79         689 push @$r, [$s,$p,$o];
74 79         1694 $self->result($r);
75             } # _triple
76              
77             sub start_element
78             {
79 104     104 0 65369 my ($self, $sax) = @_;
80 104         158 DEBUG && print STDERR " FFF start_element($sax->{LocalName})\n";
81 104         135 DEBUG && print STDERR Dumper($sax->{Attributes});
82 104 100       305 if ($sax->{LocalName} eq 'RDF')
83             {
84             # This is the toplevel element of the RDF document. See if there
85             # is an xml:base URL specified:
86 12         40 foreach my $rh (values %{$sax->{Attributes}})
  12         74  
87             {
88 38 100 66     164 if (($rh->{Prefix} eq 'xml') && ($rh->{LocalName} eq 'base'))
89             {
90             # Found the xml:base!
91 2         10 $self->addns(q{_perl_module_rdf_simple_base_} => $rh->{Value});
92             } # if
93             } # foreach
94             } # if
95 104         148 my $e;
96 104         2892 my $stack = $self->stack;
97 104         851 my $parent;
98 104 100       239 if (scalar(@$stack) > 0)
99             {
100 92         156 $parent = $stack->[-1];
101             }
102             my $attrs = RDF::Simple::Parser::Attribs->new($sax->{Attributes},
103 104         2367 $self->qnames);
104             # Add namespace to our lookup table:
105 104         305 $self->addns($sax->{Prefix} => $sax->{NamespaceURI});
106             $e = RDF::Simple::Parser::Element->new(
107             $sax->{NamespaceURI},
108             $sax->{Prefix},
109             $sax->{LocalName},
110 104         2443 $parent,
111             $attrs,
112             qnames => $self->qnames,
113             base => $self->base,
114             );
115 104         165 push @{$e->xtext}, $e->qname.$e->attrs;
  104         2168  
116 104         1934 push @{$stack}, $e;
  104         207  
117 104         2264 $self->stack($stack);
118             } # start_element
119              
120             sub characters
121             {
122 187     187 0 13981 my ($self, $chars) = @_;
123 187   50     450 my $stack = $self->{stack} || [];
124 187         532 $stack->[-1]->{text} .= $chars->{Data};
125 187         505 $stack->[-1]->{xtext}->[-1] .= $chars->{Data};
126 187         4342 $self->stack($stack);
127             } # characters
128              
129             sub end_element
130             {
131 104     104 0 14097 my ($self, $sax) = @_;
132 104         192 my $name = $sax->{LocalName};
133 104         163 my $qname = $sax->{Name};
134 104         131 DEBUG && print STDERR " FFF end_element($name,$qname)\n";
135 104         2489 my $stack = $self->stack;
136 104         771 my $element = pop @{$stack};
  104         162  
137             # DEBUG && print STDERR " DDD element is ", Dumper($element);
138 104         355 $element->{xtext}->[2] .= '{qname}.'>';
139 104 100       274 if (scalar(@$stack) > 0)
140             {
141 92   100     1993 my $kids = $stack->[-1]->children || [];
142 92         853 push @$kids, $element;
143 92         1955 $stack->[-1]->children($kids);
144 92         695 @{ $element->{xtext} } = grep { defined($_) } @{ $element->{xtext} };
  92         244  
  276         512  
  92         188  
145 92         137 $stack->[-1]->{xtext}->[1] = join('', @{$element->{xtext}});
  92         303  
146 92         1958 $self->stack($stack);
147             }
148             else
149             {
150 12         48 $self->document($element);
151             }
152             } # end_element
153              
154             =item uri
155              
156             Takes a URI (possibly relative to the current RDF document)
157             and returns an absolute URI.
158              
159             =cut
160              
161             sub uri
162             {
163 110     110 1 1438 my ($self, $uri) = @_;
164 110   100     207 my $sBase = $self->ns->uri('_perl_module_rdf_simple_base_') || q{};
165 110 50 66     460 if ($uri =~ m/\A:/)
    100          
166             {
167             # URI has empty base.
168 0         0 $uri = qq{$sBase$uri};
169             } # if
170             elsif (($uri =~ m/\A#/) && defined $sBase)
171             {
172             # URI has empty base.
173 11         30 $uri = qq{$sBase$uri};
174             } # if
175 110         1161 return $uri;
176             } # uri
177              
178             sub bNode
179             {
180 11     11 0 49 my ($self, $id, %p) = @_;
181 11         182 my $n_id = sprintf("_:id%08x%04x", time, int rand 0xFFFF);
182 11 50       272 $n_id = $self->bnode_absolute_prefix.$n_id if $self->bnode_absolute_prefix;
183 11         227 return $n_id;
184             } # bNode
185              
186             sub literal
187             {
188 45     45 0 1004 my ($self, $string, $attrs) = @_;
189 45         80 DEBUG && print STDERR " FFF literal()\n";
190 45 0 33     110 if ($attrs->{lang} and $attrs->{dtype})
191             {
192 0         0 die "can't have both lang and dtype";
193             } # if
194 45         110 return $string;
195             #r_quot = re.compile(r'([^\\])"')
196             # return ''.join(('"%s"' %
197             # r_quot.sub('\g<1>\\"',
198             #`unicode(s)`[2:-1]),
199             # lang and ("@" + lang) or '',
200             # dtype and ("^^<%s>" % dtype) or ''))
201             } # literal
202              
203             sub document
204             {
205 12     12 0 33 my ($self, $doc) = @_;
206 12 50       272 warn("couldn't find rdf:RDF element") unless $doc->URI eq $self->ns->uri('rdf').'RDF';
207 12 100       343 my @children = @{$doc->children} if $doc->children;
  11         341  
208 12 100       135 unless (scalar(@children) > 0)
209             {
210 1         13 warn("no rdf triples found in document!");
211 1         14 return;
212             }
213 11         31 foreach my $e (@children)
214             {
215             # DEBUG && print STDERR Dumper($e);
216 27         85 $self->nodeElement($e);
217             } # foreach
218             } # document
219              
220              
221             sub nodeElement
222             {
223 34     34 0 70 my ($self, $e) = @_;
224 34         750 my $dissed = $self->disallowed;
225 34         269 my $dis = grep {$_ eq $e->URI} @$dissed;
  443         11244  
226 34 50       309 warn("disallowed element used as node") if $dis;
227 34         79 my $rdf = $self->ns->uri('rdf');
228 34   50     1183 my $base = $e->base || $self->base || q{};
229 34 100       1887 if ($e->attrs->{$rdf.'ID'})
    100          
    100          
    100          
230             {
231 3         93 $e->subject( $self->uri($base .'#'. $e->attrs->{$rdf.'ID'}));
232             }
233             elsif ($e->attrs->{$rdf.'about'})
234             {
235 24         1347 $e->subject( $self->uri( $e->attrs->{$rdf.'about'} ));
236             }
237             elsif ($e->attrs->{$rdf.'nodeID'})
238             {
239 3         261 $e->subject( $self->bNode($e->attrs->{$rdf.'nodeID'}) );
240             }
241             elsif (not $e->subject)
242             {
243 3         230 $e->subject($self->bNode);
244             }
245 34 100       1002 if ($e->URI ne $rdf.'Description')
246             {
247 13         397 $self->_triple($e->subject, $rdf.'type', $self->uri($e->URI));
248             }
249 34 50       971 if ($e->attrs->{$rdf.'type'})
250             {
251 0         0 $self->_triple($e->subject, $rdf.'type', $self->ns->uri($e->{$rdf.'type'}));
252             }
253 34         307 foreach my $k (keys %{$e->attrs})
  34         707  
254             {
255 31         939 my $dis = $self->disallowed;
256 31         275 push @$dis, $rdf.'type';
257 31         123 my ($in) = grep {/$k/} @$dis;
  432         1313  
258 31 50       102 if (not $in)
259             {
260 0         0 my $objt = $self->literal($e->attrs->{$k}, $e->language);
261 0         0 DEBUG && print STDERR " DDD nodeElement _triple(,,$objt)\n";
262 0         0 $self->_triple($e->subject, $self->uri($k), $objt);
263             } # if
264             } # foreach
265 34         810 my $children = $e->children;
266 34         283 foreach my $child (@$children)
267             {
268 57         133 $self->propertyElt($child);
269             } # foreach
270             } # nodeElement
271              
272              
273             sub propertyElt
274             {
275 57     57 0 92 my $self = shift;
276 57         81 my $e = shift;
277 57         86 DEBUG && print STDERR " FFF propertyElt($e)\n";
278             # DEBUG && print STDERR Dumper($e);
279 57         118 my $rdf = $self->ns->uri('rdf');
280 57 100       1319 if ($e->URI eq $rdf.'li')
281             {
282 11   100     267 $e->parent->{liCounter} ||= 1;
283 11         262 $e->URI($rdf.$e->parent->{liCounter});
284 11         296 $e->parent->{liCounter}++;
285             }
286 57   100     1661 my $children = $e->children || [];
287 57 100       1629 if ($e->attrs->{$rdf.'resource'})
288             {
289             # This is an Object Property Declaration Axiom.
290 6         183 $self->_triple($e->parent->subject, $self->uri($e->URI), $e->attrs->{$rdf.'resource'});
291 6         66 return;
292             }
293 51 100 100     551 if (
294             (scalar(@$children) == 1)
295             &&
296             (! $e->attrs->{$rdf.'parseType'})
297             )
298             {
299 3         28 $self->resourcePropertyElt($e);
300 3         27 return;
301             }
302 48 100 100     1046 if ((scalar(@$children) eq 0) && (defined $e->text) && ($e->text ne q{}))
      66        
303             {
304 44         1612 $self->literalPropertyElt($e);
305 44         537 return;
306             }
307 4         147 my $ptype = $e->attrs->{$rdf.'parseType'};
308 4 100       40 if ($ptype)
309             {
310 3 100       15 if ($ptype eq 'Resource')
311             {
312 1         4 $self->parseTypeResourcePropertyElt($e);
313 1         7 return;
314             }
315 2 100       7 if ($ptype eq 'Collection')
316             {
317 1         5 $self->parseTypeCollectionPropertyElt($e);
318 1         9 return;
319             }
320 1         5 $self->parseTypeLiteralOrOtherPropertyElt($e);
321 1         13 return;
322             } # if has a parseType
323 1 50 33     23 if ((! defined $e->text) || ($e->text eq q{}))
324             {
325             # DEBUG && print STDERR Dumper($e);
326 1         15 $self->emptyPropertyElt($e);
327 1         17 return;
328             } # if
329 0         0 delete $e->{parent};
330 0         0 warn " WWW failed to parse element: ", Dumper($e);
331             } # propertyElt
332              
333             sub resourcePropertyElt
334             {
335 3     3 0 6 my ($self, $e) = @_;
336 3         4 DEBUG && print STDERR " FFF resourcePropertyElt($e)\n";
337             # DEBUG && print STDERR Dumper($e);
338 3         6 my $rdf = $self->ns->uri('rdf');
339 3         63 my $n = $e->children->[0];
340 3         27 $self->nodeElement($n);
341 3 50       57 if ($e->parent)
342             {
343 3         70 $self->_triple($e->parent->subject, $self->uri($e->URI), $n->subject);
344             }
345 3 50       68 if ($e->attrs->{$rdf.'ID'})
346             {
347 0   0     0 my $base = $e->base || $self->base;
348 0         0 my $i = $self->uri($base .'#'. $e->attrs->{$rdf.'ID'});
349 0         0 $self->reify($i, $e->parent->subject, $self->uri($e->URI), $n->subject);
350             } # if
351             } # resourcePropertyElt
352              
353              
354             sub reify
355             {
356 0     0 0 0 my ($self,$r,$s,$p,$o) = @_;
357 0         0 my $rdf = $self->ns->uri('rdf');
358 0         0 a $self->_triple($r, $self->uri($rdf.'subject'), $s);
359 0         0 $self->_triple($r, $self->uri($rdf.'predicate'), $p);
360 0         0 $self->_triple($r, $self->uri($rdf.'object'), $o);
361 0         0 $self->_triple($r, $self->uri($rdf.'type'), $self->uri($rdf.'Statement'));
362             } # reify
363              
364              
365             sub literalPropertyElt
366             {
367 44     44 0 80 my ($self, $e) = @_;
368 44         56 DEBUG && print STDERR " FFF literalPropertyElt($e)\n";
369 44   33     989 my $base = $e->base || $self->base;
370 44         1565 my $rdf = $self->ns->uri('rdf');
371 44         1007 my $o = $self->literal($e->text, $e->language, $e->attrs->{$rdf.'datatype'});
372 44         126 DEBUG && print STDERR " DDD literalPropertyElt _triple(,,$o)\n";
373 44         983 $self->_triple($e->parent->subject, $self->uri($e->URI), $o);
374 44 50       1231 if ($e->attrs->{$rdf.'ID'})
375             {
376 0         0 my $i = $self->uri($base .'#'. $e->attrs->{$rdf.'ID'});
377 0         0 $self->reify($i, $e->parent->subject, $self->uri($e->URI), $o);
378             } # if
379             } # literalPropertyElt
380              
381             sub parseTypeLiteralOrOtherPropertyElt {
382 1     1 0 3 my ($self,$e) = @_;
383 1         1 DEBUG && print STDERR " FFF parseTypeLiteralOrOtherPropertyElt($e)\n";
384 1   33     23 my $base = $e->base || $self->base;
385 1         44 my $rdf = $self->ns->uri('rdf');
386 1         24 my $o = $self->literal($e->xtext->[1],$e->language,$rdf.'XMLLiteral');
387 1         2 DEBUG && print STDERR " DDD parseTypeLiteralOrOtherPropertyElt _triple(,,$o)\n";
388 1         22 $self->_triple($e->parent->subject,$self->uri($e->URI),$o);
389 1 50       24 if ($e->attrs->{$rdf.'ID'}) {
390 0         0 my $i = $self->uri($base .'#'. $e->attrs->{$rdf.'ID'});
391 0         0 $e->subject($i);
392 0         0 $self->reify($i,$e->parent->subject,$self->URI($e->URI),$o);
393             }
394             }
395              
396             sub parseTypeResourcePropertyElt
397             {
398 1     1 0 2 my ($self,$e) = @_;
399 1         1 DEBUG && print STDERR " FFF parseTypeResourcePropertyElt($e)\n";
400 1         3 my $n = $self->bNode;
401 1         3 DEBUG && print STDERR " DDD parseTypeResourcePropertyElt _triple(,,$n)\n";
402 1         19 $self->_triple($e->parent->subject, $self->uri($e->URI), $n);
403 1         24 my $c = RDF::Simple::Parser::Element->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#',
404             'rdf',
405             'Description',
406             $e->parent,
407             $e->attrs,
408             qnames => $self->qnames,
409             base => $e->base,
410             );
411 1         20 $c->subject($n);
412 1         6 my @c_children;
413 1         20 my $children = $e->children;
414 1         7 foreach (@$children)
415             {
416 2         37 $_->parent($c);
417 2         14 push @c_children, $_;
418             }
419 1         20 $c->children(\@c_children);
420 1         8 $self->nodeElement($c);
421             } # parseTypeResourcePropertyElt
422              
423             sub parseTypeCollectionPropertyElt
424             {
425 1     1 0 2 my ($self,$e) = @_;
426 1         2 DEBUG && print STDERR " FFF parseTypeCollectionPropertyElt($e)\n";
427 1         3 my $rdf = $self->ns->uri('rdf');
428 1         21 my $children = $e->children;
429 1         8 my @s;
430 1         3 foreach (@$children)
431             {
432 3         7 $self->nodeElement($_);
433 3         7 push @s, $self->bNode;
434             }
435 1 50       4 if (scalar(@s) eq 0)
436             {
437 0         0 $self->_triple($e->parent->subject,$self->uri($e->URI),$self->uri($rdf.'nil'));
438             }
439             else
440             {
441 1         21 $self->_triple($e->parent->subject,$self->uri($e->URI),$s[0]);
442 1         7 foreach my $n (@s)
443             {
444 3         26 $self->_triple($n,$self->uri($rdf.'type'),$self->uri($rdf.'List'));
445             }
446 1         9 for (0 .. $#s)
447             {
448 3         21 $self->_triple($s[$_],$self->uri($rdf.'first'),$e->children->[$_]->subject);
449             }
450 1         10 for (0 .. ($#s-1))
451             {
452 2         12 $self->_triple($s[$_],$self->uri($rdf.'rest'),$s[$_+1]);
453             }
454 1         17 $self->_triple($s[-1],$self->uri($rdf.'rest'),$self->uri($rdf.'nil'));
455             }
456             } # parseTypeCollectionPropertyElt
457              
458              
459             sub emptyPropertyElt
460             {
461 1     1 0 2 my $self = shift;
462 1         2 my $e = shift;
463 1         2 DEBUG && print STDERR " FFF emptyPropertyElt($e)\n";
464             # DEBUG && print STDERR Dumper($e);
465 1         11 my $rdf = $self->ns->uri('rdf');
466 1 50       29 my $base = $e->base or $self->base;
467 1   50     44 $base ||= '';
468 1         2 my @keys = keys %{$e->attrs};
  1         22  
469 1         12 my $ids = $rdf.'ID';
470 1         4 my ($id) = grep {/$ids/} @keys;
  0         0  
471 1         3 my $r;
472 1 50       3 if ($id)
473             {
474 0         0 $r = $self->literal($e->text, $e->language); # was o
475 0         0 DEBUG && print STDERR " DDD emptyPropertyElt _triple(,,$r)\n";
476 0         0 $self->_triple($e->parent->subject, $self->uri($e->URI), $r);
477             }
478             else
479             {
480 1 50       24 if ($e->attrs->{$rdf.'resource'})
    50          
481             {
482 0         0 my $res = $e->attrs->{$rdf.'resource'};
483 0   0     0 $res ||= '';
484 0 0       0 $res = $base.$res if $res !~ m/\:\/\//;
485 0         0 $r = $self->uri($res);
486             }
487             elsif ($e->attrs->{$rdf.'nodeID'})
488             {
489 0         0 $r = $self->bNode($e->attrs->{$rdf.'nodeID'});
490             }
491             else
492             {
493 1         43 DEBUG && print STDERR " DDD element has no 'resource' attr and no 'nodeID' attr.\n";
494             # Generate a new node ID, in case this empty element has attributes:
495 1         3 $r = $self->bNode;
496             }
497 1         34 my $dis = $self->disallowed;
498 1         9 my @a = map { grep {!/$_/} @$dis } keys %{$e->attrs};
  0         0  
  0         0  
  1         24  
499 1 50       13 if (scalar(@a) < 1)
500             {
501             # This empty element has no attributes, nothing to declare.
502             # Just add empty string to the triple:
503 1         3 $r = q{};
504             } # if
505 1         3 foreach my $a (@a)
506             {
507 0 0       0 if ($a ne $rdf.'type')
508             {
509 0         0 my $o = $self->literal($e->attrs->{$a}, $e->language);
510 0         0 DEBUG && print STDERR " DDD emptyPropertyElt _triple(,,$o)\n";
511 0         0 $self->_triple($r, $self->uri($a), $o);
512             } # if
513             else
514             {
515 0         0 $self->_triple($r, $self->uri($rdf.'type'), $self->uri($e->attrs->{$a}));
516             }
517             } # foreach
518 1         25 $self->_triple($e->parent->subject, $self->uri($e->URI), $r);
519             } # else ! $id
520 1 50       31 if ($e->attrs->{$rdf.'ID'})
521             {
522 0           my $i = $self->uri($base .'#'. $e->attrs->{$rdf.'ID'});
523 0           $self->reify($i, $e->parent->subject, $self->uri($e->URI,$r));
524             }
525             } # emptyPropertyElt
526              
527              
528             =back
529              
530             =head1 NOTES
531              
532             This parser is a transliteration of
533             Sean B Palmer's python RDF/XML parser:
534              
535             http://www.infomesh.net/2003/rdfparser/
536              
537             Thus the idioms inside are a bit pythonic.
538             Most credit for the effort is due to sbp.
539              
540             =cut
541              
542             1;
543              
544             __END__