File Coverage

blib/lib/RDF/Core/Parser.pm
Criterion Covered Total %
statement 93 567 16.4
branch 0 254 0.0
condition 0 83 0.0
subroutine 31 91 34.0
pod n/a
total 124 995 12.4


line stmt bran cond sub pod time code
1             #
2             # The contents of this file are subject to the Mozilla Public
3             # License Version 1.1 (the "License"); you may not use this file
4             # except in compliance with the License. You may obtain a copy of
5             # the License at http://www.mozilla.org/MPL/
6             #
7             # Software distributed under the License is distributed on an "AS
8             # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9             # implied. See the License for the specific language governing
10             # rights and limitations under the License.
11             #
12             # The Original Code is the RDF::Core module
13             #
14             # The Initial Developer of the Original Code is Ginger Alliance Ltd.
15             # Portions created by Ginger Alliance are
16             # Copyright (C) 2001 Ginger Alliance Ltd.
17             # All Rights Reserved.
18             #
19             # Contributor(s):
20             #
21             # Alternatively, the contents of this file may be used under the
22             # terms of the GNU General Public License Version 2 or later (the
23             # "GPL"), in which case the provisions of the GPL are applicable
24             # instead of those above. If you wish to allow use of your
25             # version of this file only under the terms of the GPL and not to
26             # allow others to use your version of this file under the MPL,
27             # indicate your decision by deleting the provisions above and
28             # replace them with the notice and other provisions required by
29             # the GPL. If you do not delete the provisions above, a recipient
30             # may use your version of this file under either the MPL or the
31             # GPL.
32             #
33              
34             package RDF::Core::Parser;
35              
36 1     1   5 use strict;
  1         1  
  1         32  
37 1     1   4 use warnings;
  1         2  
  1         52  
38              
39             require URI;
40             require XML::Parser;
41              
42 1     1   6 use RDF::Core::Constants qw (:xml :rdf);
  1         1  
  1         250  
43 1     1   6 use Carp;
  1         1  
  1         71  
44              
45             ########################################
46             # constants
47 1     1   6 use constant PARSE_LITERAL => "Literal";
  1         2  
  1         78  
48 1     1   6 use constant PARSE_RESOURCE => "Resource";
  1         8  
  1         90  
49 1     1   5 use constant PARSE_COLLECTION => "Collection";
  1         2  
  1         43  
50              
51 1     1   5 use constant F_IDLE => 0;
  1         2  
  1         45  
52 1     1   5 use constant F_PARSING => 1;
  1         2  
  1         47  
53 1     1   42 use constant F_HASROOT => 2;
  1         2  
  1         41  
54              
55             ########################################
56             # rdf types
57 1     1   5 use constant RDFT_UNKNOWN => 0;
  1         1  
  1         44  
58              
59 1     1   4 use constant RDFT_BAG => 1;
  1         2  
  1         40  
60 1     1   5 use constant RDFT_SEQ => 2;
  1         1  
  1         44  
61 1     1   5 use constant RDFT_ALT => 4;
  1         1  
  1         73  
62 1     1   5 use constant RDFT_CONTAINER => RDFT_BAG | RDFT_SEQ | RDFT_ALT;
  1         1  
  1         96  
63              
64             my %RDF_TYPES = (RDF_BAG() => RDFT_BAG,
65             RDF_SEQ() => RDFT_SEQ,
66             RDF_ALT() => RDFT_ALT,
67             );
68              
69             ########################################
70             # RDF node types (according to spec.)
71 1     1   6 use constant NODE_UNKNOWN => 0;
  1         2  
  1         39  
72              
73 1     1   5 use constant NODE_RDF => 1;
  1         2  
  1         50  
74              
75 1     1   5 use constant NODE_DESCRIPTION => 2;
  1         1  
  1         41  
76              
77 1     1   4 use constant NODE_BAG => 4;
  1         1  
  1         45  
78 1     1   4 use constant NODE_SEQ => 8;
  1         2  
  1         37  
79 1     1   5 use constant NODE_ALT => 16;
  1         1  
  1         75  
80 1     1   5 use constant NODE_CONTAINER => NODE_BAG | NODE_SEQ | NODE_ALT;
  1         1  
  1         47  
81              
82 1     1   6 use constant NODE_PROPERTY => 32;
  1         1  
  1         59  
83 1     1   6 use constant NODE_PROPERTY_1 => 64; #simple w/ value
  1         2  
  1         56  
84 1     1   4 use constant NODE_PROPERTY_2 => 128; #simple literal
  1         16  
  1         45  
85 1     1   4 use constant NODE_PROPERTY_3 => 256; #parse resource
  1         2  
  1         47  
86 1     1   5 use constant NODE_PROPERTY_4 => 512; #with id/resource
  1         1  
  1         51  
87 1     1   4 use constant NODE_PROPERTY_5 => 4096; #parse collection
  1         2  
  1         81  
88 1         57 use constant NODE_PROPERTY_MASK => NODE_PROPERTY_1 | NODE_PROPERTY_2 |
89 1     1   7 NODE_PROPERTY_3 | NODE_PROPERTY_4 | NODE_PROPERTY_5;
  1         2  
90              
91 1     1   5 use constant NODE_TYPED => 1024;
  1         2  
  1         89  
92              
93 1     1   5 use constant NODE_OBJ => NODE_DESCRIPTION | NODE_CONTAINER | NODE_TYPED;
  1         1  
  1         6586  
94              
95             our %RDF_TYPECONST = (Bag => NODE_BAG,
96             Seq => NODE_SEQ,
97             Alt => NODE_ALT,
98             );
99              
100             our %RDF_TYPENAMES = reverse %RDF_TYPECONST;
101              
102             ########################################
103             # ordinary methods
104             sub new {
105 0     0     my ($class, %params) = @_;
106 0   0       $class = ref $class || $class;
107 0 0         if ($params{InlineURI}) {
108 0           carp "InlineURI parameter is deprecated, use BNodePrefix instead";
109             }
110 0           my $self = {
111             assert => $params{Assert},
112             baseuri => $params{BaseURI},
113             bnode => $params{BNodePrefix},
114             nodeid => {}
115             };
116 0           bless $self, $class;
117 0           return $self;
118             }
119              
120             sub parse {
121 0     0     my ($self, $what) = @_;
122             #be careful about the circular reference
123 0           my $expatParser = $self->_createExpatParser;
124 0           $self->_setFlag(F_PARSING);
125 0           $expatParser->parse($what);
126 0           $self->_clearFlag(F_PARSING);
127             }
128              
129             sub parseFile {
130 0     0     my ($self, $filename) = @_;
131 0           my $expatParser = $self->_createExpatParser;
132 0           $self->_setFlag(F_PARSING);
133 0           $expatParser->parsefile($filename);
134 0           $self->_clearFlag(F_PARSING);
135             }
136              
137             ########################################
138             # tools
139             # attribure processing
140             sub _getNamespaces {
141 0     0     my ($self, $attrs) = @_;
142 0           my %ret;
143 0           foreach my $name (keys %$attrs) {
144 0 0         if ($name =~ /xmlns:?(.*)/) {
145 0   0       my $abbr = $1 || '';
146 0           $ret{$abbr} = $$attrs{$name};
147 0           delete $$attrs{$name};
148             }
149             }
150 0           return \%ret;
151             }
152              
153             sub _expandAttributes {
154 0     0     my ($self, $element, %attrs) = @_;
155 0           my $ret = {};
156 0           foreach my $name (keys %attrs) {
157             #expand name and store
158 0           $name =~ /((.*):|^)(.*)/;
159 0           my ($ns, $local) = ($2, $3);
160 0 0         $ns = $ns ? $self->_findNS($ns) : $element->{ns};
161 0           $$ret{$ns . $local} = {
162             name => $local,
163             ns => $ns, #
164             value => $attrs{$name},
165             };
166             }
167 0           return $ret;
168             }
169              
170             sub _getElementResource {
171 0     0     my ($self, $element) = @_;
172 0           undef my $ret;
173              
174 0 0         if ($element->{resource}) {
    0          
    0          
175 0           $ret = $element->{resource};
176             }
177             elsif ($element->{nodeid}) {
178 0           $ret = $self->_getImplicitURI($element->{nodeid});
179             }
180             elsif ($element->{rnode}) {
181 0           $ret = $self->_uri($element->{rnode});
182             }
183              
184 0           return $ret;
185             }
186              
187             # miscellaneous
188             sub _localName {
189 0     0     my ($self, $in) = @_;
190 0           $in =~ /((.*):|^)(.*)/;
191 0           return $3;
192             }
193              
194             sub _nsAbbr {
195 0     0     my ($self, $in) = @_;
196 0           $in =~ /((.*):|^)(.*)/;
197 0   0       return $2 || '';
198             }
199              
200             sub _uri {
201 0     0     my ($self, $element) = @_;
202 0   0       return $element->{uri} ||
203             ($element->{uri} = $self->_getImplicitURI($element->{nodeid}));
204             }
205              
206             sub _validFirstLevel {
207 0     0     my ($self, $element) = @_;
208 0           return $element->{qname} eq RDF_DESCRIPTION;
209             }
210              
211             sub _doAssert {
212 0     0     my ($self, $subject, $params, $stmt) = @_;
213 0           my %params = %$params;
214             #ordinary assertion
215 0 0         unless ($subject->{abouteach}) {
216 0           &{$self->{assert}}(%params);
  0            
217             }
218             #about each caching
219             else {
220 0           my $slot = ${$self->{abouteach}}{$subject->{abouturi}};
  0            
221 0 0         $slot = ${$self->{abouteach}}{$subject->{abouturi}} = []
  0            
222             unless $slot;
223 0           my %foo = map {($_, $params{$_})} grep {$_ !~ /^subject/} keys %params;
  0            
  0            
224 0           push @$slot, {%foo};
225             }
226             #reification
227 0 0 0       if ($subject->{bagid} || $stmt) {
228 0   0       my $suri = $stmt || $self->_getImplicitURI;
229 0           my $stmt = {
230             statement_uri => $suri,
231             %params,
232             };
233 0           $self->_assertReification($stmt);
234 0 0         if ($subject->{bagid}) {
235 0           my $buri = $subject->{baguri};
236 0 0         $self->{urimembers}{$buri} = []
237             unless exists $self->{urimembers}{$buri};
238 0           push @{$self->{urimembers}{$buri}}, $suri;
  0            
239             #collect bag members for later assertion
240 0           push @{$subject->{bagmembers}}, $suri;
  0            
241             }
242             }
243             }
244              
245             sub _assertReification {
246 0     0     my ($self, $stmt) = @_;
247             #type
248 0           my $params = {subject_uri => $stmt->{statement_uri},
249             predicate_ns => RDF_NS,
250             predicate_name => 'type',
251             predicate_uri => RDF_TYPE,
252             object_ns => RDF_NS,
253             object_name => 'Statement',
254             object_uri => RDF_STATEMENT,
255             };
256 0           $self->_doAssert({}, $params);
257             #subject
258 0           $params = {subject_uri => $stmt->{statement_uri},
259             predicate_ns => RDF_NS,
260             predicate_name => 'subject',
261             predicate_uri => RDF_SUBJECT,
262             object_uri => $stmt->{subject_uri},
263             };
264 0 0         $params->{object_ns} = $stmt->{subject_ns} if $stmt->{subject_ns};
265 0 0         $params->{object_name} = $stmt->{subject_name} if $stmt->{subject_name};
266 0           $self->_doAssert({}, $params);
267             #predicate
268 0           $params = {subject_uri => $stmt->{statement_uri},
269             predicate_ns => RDF_NS,
270             predicate_name => 'predicate',
271             predicate_uri => RDF_PREDICATE,
272             object_uri => $stmt->{predicate_uri},
273             };
274 0 0         $params->{object_ns} = $stmt->{predicate_ns} if $stmt->{predicate_ns};
275 0 0         $params->{object_name} = $stmt->{predicate_name} if $stmt->{predicate_name};
276 0           $self->_doAssert({}, $params);
277             #object
278 0           $params = {subject_uri => $stmt->{statement_uri},
279             predicate_ns => RDF_NS,
280             predicate_name => 'object',
281             predicate_uri => RDF_OBJECT,
282             };
283 0 0         if ($stmt->{object_uri}) {
284 0           $params->{object_uri} = $stmt->{object_uri};
285 0 0         $params->{object_ns} = $stmt->{object_ns} if $stmt->{object_ns};
286 0 0         $params->{object_name} = $stmt->{object_name} if $stmt->{object_name};
287             } else {
288 0           $params->{object_literal} = $stmt->{object_literal};
289 0           $params->{object_datatype} = $stmt->{object_datatype};
290 0           $params->{object_lang} = $stmt->{object_lang};
291             }
292 0           $self->_doAssert({}, $params);
293             }
294              
295             sub _assertAttributes {
296 0     0     my ($self, $subject, $attrs) = @_;
297             #foreach my $attr (grep {$_->{ns} ne RDF_NS} values %$attrs) {
298 0           foreach my $attr (values %$attrs) {
299 0           my $qname = $attr->{ns} . $attr->{name};
300             #test member for ebaoutEach on ID
301 0           my $re = '^' . RDF_NS . '_\d+$';
302 0 0         if ($qname =~ /$re/) {
303             #it seems there is nothing to catch for ID, because
304             #this member is literal and has no resource
305             }
306             #assert
307 0 0 0       my %params = (
308             subject_uri => $self->_uri($subject),
309             predicate_ns => $attr->{ns},
310             predicate_name => $attr->{name},
311             predicate_uri => $qname,
312             object_literal =>
313             defined $attr->{value} ? $attr->{value} : "",
314             object_lang => $self->_findLang() || "",
315             object_datatype => "",
316             );
317 0           $self->_doAssert($subject, \%params);
318             }
319             }
320              
321             sub _assertPropAttrs {
322 0     0     my ($self, $element, $attrs) = @_;
323 0           my $type = $element->{type};
324             #if (($type == NODE_DESCRIPTION) || ($type == NODE_TYPED)) {
325 0 0         if ($type & NODE_OBJ) {
    0          
326 0           my $subject = ${$self->{subjects}}[-1];
  0            
327 0           $self->_assertAttributes($subject, $attrs);
328             }
329             elsif ($type == NODE_PROPERTY_4) {
330 0           my $subject = {uri => $self->_getElementResource($element)};
331 0           $self->_assertAttributes($subject, $attrs);
332             }
333             }
334              
335             sub _assertRDFAttrs {
336 0     0     my ($self, $element, $attrs) = @_;
337 0           my $type = $element->{type};
338 0 0         if ($type == NODE_TYPED) {
    0          
    0          
339 0           $self->_assertRDFTypeElement($element, $element);
340             }
341             elsif ($type & NODE_CONTAINER) {
342 0           $self->_assertRDFType($element, $type);
343             }
344             elsif ($element->{rdftype}) {
345 0           $self->_assertRDFTypeString($element, $element->{rdftype});
346 0           my $ctype = $RDF_TYPES{$element->{rdftype}};
347 0 0 0       $element->{containertype} = $ctype if $ctype and RDFT_CONTAINER;
348             }
349             }
350              
351             sub _assertElement {
352 0     0     my ($self, $expat, $subject, $element) = @_;
353 0           my $uri;
354              
355 0 0         if ($element->{type} == NODE_PROPERTY_5) {
356             #Collection - prepare assertion for rdf:nil terminator
357 0 0         if ($element->{collast}) {
358 0           $subject = {uri=>$$element{collast}};
359 0           $element = {ns=>RDF_NS, name=>'rest', qname=>RDF_REST};
360             }
361 0           $uri=RDF_NIL;
362             } else {
363             #Other then collection properties
364 0           $uri = $self->_getElementResource($element);
365             }
366              
367 0 0 0       if ($element->{resource} && __trim($element->{text})) {
368 0           $expat->xpcroak("predicate has both of resource and literal");
369             }
370 0 0 0       if ($element->{datatype} && $uri) {
371 0           $expat->xpcroak("invalid rdf:datatype use");
372             }
373 0           my %object;
374 0 0         if ($uri) {
375 0           %object = (object_uri => $uri);
376             } else {
377 0 0 0       %object = (object_literal =>
      0        
378             defined $element->{text} ? $element->{text} : "",
379             object_datatype =>$element->{datatype} || "",
380             object_lang => $self->_findLang($element) || "",
381             );
382             }
383              
384 0           my %params = (
385             subject_uri => $self->_uri($subject),
386             predicate_ns => $element->{ns},
387             predicate_name => $element->{name},
388             predicate_uri => $element->{qname},
389             %object,
390             );
391              
392 0           $self->_doAssert($subject, \%params, $element->{uri});
393             }
394              
395             sub _assertRDFType {
396 0     0     my ($self, $subject, $type) = @_;
397 0           my %params = (
398             subject_uri => $self->_uri($subject),
399             predicate_ns => RDF_NS,
400             predicate_name => 'type',
401             predicate_uri => RDF_TYPE,
402             object_ns => RDF_NS,
403             object_name => $RDF_TYPENAMES{$type},
404             object_uri => RDF_NS . $RDF_TYPENAMES{$type},
405             );
406 0           $self->_doAssert($subject, \%params);
407             }
408              
409             sub _assertRDFTypeString {
410 0     0     my ($self, $subject, $string) = @_;
411 0           my %params = (
412             #subject_ns => $subject->{ns},
413             #subject_name => $subject->{name},
414             subject_uri => $self->_uri($subject),
415             predicate_ns => RDF_NS,
416             predicate_name => 'type',
417             predicate_uri => RDF_TYPE,
418             object_uri => $string,
419             );
420 0           $self->_doAssert($subject, \%params);
421             }
422              
423             sub _assertRDFTypeElement {
424 0     0     my ($self, $subject, $element) = @_;
425 0           my %params = (
426             #subject_ns => $subject->{ns},
427             #subject_name => $subject->{name},
428             subject_uri => $self->_uri($subject),
429             predicate_ns => RDF_NS,
430             predicate_name => 'type',
431             predicate_uri => RDF_TYPE,
432             object_ns => $element->{ns},
433             object_name => $element->{name},
434             object_uri => $element->{qname},
435             );
436 0           $self->_doAssert($subject, \%params);
437             }
438              
439             sub _assertAboutEach {
440 0     0     my $self = shift;
441 0           for my $m (keys %{$self->{urimembers}}) {
  0            
442 0           my $members = $self->{urimembers}{$m};
443 0           my $abouts = $self->{abouteach}{$m};
444 0 0         next unless $abouts;
445 0           for my $a (@$abouts) {
446 0           my %aparams = %$a;
447 0           for my $s (@$members) {
448 0           my %params = %aparams;
449 0           $params{subject_uri} = $s;
450             #assert w/ empty subject
451 0           $self->_doAssert({}, \%params);
452             }
453             }
454             }
455             }
456              
457             sub _assertCollectionItem {
458 0     0     my ($self, $subject, $predicate, $item) = @_;
459              
460 0           my $colItem = $self->_getImplicitURI;
461 0 0         if ($predicate->{collast}) {
462 0           my %params = (
463             subject_uri => $predicate->{collast},
464             predicate_ns => RDF_NS,
465             predicate_name => "rest",
466             predicate_uri => RDF_REST,
467             object_uri => $colItem,
468             );
469 0           $self->_doAssert({},\%params);
470             } else {
471 0           my %params = (
472             subject_uri => $subject->{uri},
473             predicate_ns => $predicate->{ns},
474             predicate_name => $predicate->{name},
475             predicate_uri => $predicate->{qname},
476             object_uri => $colItem,
477             );
478 0           $self->_doAssert($predicate,\%params, $predicate->{uri});
479             }
480 0           my %params = (
481             subject_uri => $colItem,
482             predicate_ns => RDF_NS,
483             predicate_name => "type",
484             predicate_uri => RDF_TYPE,
485             object_uri => RDF_LIST,
486             );
487 0           $self->_doAssert({},\%params);
488            
489 0           %params = (
490             subject_uri => $colItem,
491             predicate_ns => RDF_NS,
492             predicate_name => "first",
493             predicate_uri => RDF_FIRST,
494             object_uri => $item->{uri},
495             );
496 0           $self->_doAssert({},\%params);
497 0           $self->{path}[-1]{collast} = $colItem;
498             }
499              
500             sub _getLIURI {
501 0     0     my ($self, $subject) = @_;
502             #rdf:li element can appear outside rdf:Description element
503             #(i.e. $subject can be undef)
504 0   0       my $id = "_" . ++($subject || $self)->{li_counter};
505 0           return (RDF_NS, $id, RDF_NS . $id);
506             }
507              
508             sub __trim {
509 0     0     my $val = shift;
510 0 0         $val =~ s/^\s*$//sg if (defined($val));
511 0           return $val;
512             }
513              
514             sub __checkParseType {
515 0     0     my $element = shift;
516 0 0         return unless $element->{parsetype};
517 0           my $re = PARSE_LITERAL . "|" . PARSE_RESOURCE . "|" . PARSE_COLLECTION;
518 0 0         $element->{parsetype} = PARSE_LITERAL
519             unless $element->{parsetype} =~ /$re/;
520             }
521              
522             sub _updateElement {
523 0     0     my ($self, $element, $attrs) = @_;
524             #rdf attributes
525 0           my $about = delete $$attrs{+RDFA_ABOUT};
526 0           my $abouteach = delete $$attrs{+RDFA_ABOUTEACH};
527 0           my $id = delete $$attrs{+RDFA_ID};
528 0           my $nodeid = delete $$attrs{+RDFA_NODEID};
529 0           my $bagid = delete $$attrs{+RDFA_BAGID};
530 0           my $parsetype = delete $$attrs{+RDFA_PARSETYPE};
531 0           my $rdftype = delete $$attrs{+RDFA_TYPE};
532 0           my $datatype = delete $$attrs{+RDFA_DATATYPE};
533 0           my $resource = delete $$attrs{+RDFA_RESOURCE};
534 0           my $xmllang = delete $$attrs{+XMLA_LANG};
535 0           my $xmlbase = delete $$attrs{+XMLA_BASE};
536 0 0         $element->{about} = $about ? $about->{value} : undef;
537 0 0         $element->{abouteach} = $abouteach ? $abouteach->{value} : undef;
538 0 0         $element->{id} = $id ? $id->{value} : undef;
539 0 0         $element->{nodeid} = $nodeid ? $nodeid->{value} : undef;
540 0 0         $element->{bagid} = $bagid ? $bagid->{value} : undef;
541 0           $element->{bagmembers} = [];
542 0 0         $element->{parsetype} = $parsetype ? $parsetype->{value} : undef;
543 0           __checkParseType($element);
544 0 0         $element->{rdftype} = $rdftype ? $rdftype->{value} : undef;
545 0 0         $element->{datatype} = $datatype ? $datatype->{value} : undef;
546 0 0         $element->{resource} = $resource ? $resource->{value} : undef;
547 0 0         $element->{lang} = $xmllang ? $xmllang->{value} : undef;
548 0 0         $element->{baseuri} = $xmlbase ? $xmlbase->{value} : undef;
549              
550             #create uri/about-uri (from about or id)
551 0 0         if (defined $element->{about}) {
    0          
    0          
552 0           my $baseURI = new URI($self->_findBaseURI);
553 0           my $u;
554 0 0         if ($element->{about} eq '') {
555             #base uri with fragment removed
556 0           $u = $baseURI;
557 0 0         if ($baseURI->fragment) {
558 0           my $scheme = $u->scheme;
559 0           my $opaque = $u->opaque;
560 0           $u = new URI($opaque);
561 0           $u->scheme($scheme);
562             }
563             } else {
564 0           $u = new_abs URI($element->{about}, $baseURI);
565             }
566 0           $element->{uri} = $u->as_string;
567             } elsif (defined $element->{id}) {
568 0           my $baseURI = new URI($self->_findBaseURI);
569 0           my $u = new URI($baseURI);
570 0           $u->fragment($element->{id});
571 0           $element->{uri} = $u->as_string;
572             } elsif ($element->{abouteach}) {
573 0           my $u = new URI($self->_findBaseURI);
574             #$u->fragment($element->{abouteach}); _fixme_
575 0           $element->{abouturi} = $self->_findBaseURI . $element->{abouteach};
576             }
577 0 0         if ($element->{resource}) {
578 0           my $u = new URI($element->{resource});
579 0           $element->{resource} = $u->abs($self->_findBaseURI);
580             }
581              
582             #create bagid uri
583 0 0         if ($element->{bagid}) {
584 0           my $u = new URI($self->_findBaseURI);
585 0           $u->fragment($element->{bagid});
586 0           $element->{baguri} = $u->as_string;
587             }
588              
589             #rename element if it is the rdf:li (I hope it is correct)
590 0 0         if ($element->{qname} eq RDF_LI) {
591 0           my $subject = @{$self->{subjects}} > 0
  0            
592 0 0         ? ${$self->{subjects}}[-1] : undef;
593 0           my ($ns, $name, $uri) = $self->_getLIURI($subject);
594             #we rename the whole element, hopefuly it doesn't matter
595 0           $element->{ns} = $ns;
596 0           $element->{name} = $name;
597 0           $element->{qname} = $uri;
598             }
599             }
600              
601             sub _analyzePath {
602 0     0     my ($self, $expat, $attrs) = @_;
603 0           my $path = $self->{path};
604              
605             #guess the node type
606 0           my $ce = $$path[-1];
607 0           my $ct; #current type
608 0           my $re = '^' . RDF_NS . '_(\d+)$';
609             SWITCH:
610 0           for ($ce->{qname}) {
611 0 0         $_ eq RDF_RDF && do {$ct = NODE_RDF; last SWITCH;};
  0            
  0            
612 0 0         $_ eq RDF_DESCRIPTION && do {$ct = NODE_DESCRIPTION; last SWITCH;};
  0            
  0            
613 0 0         $_ eq RDF_BAG && do {$ct = NODE_BAG;
  0            
614 0           $ce->{containertype} = RDFT_BAG;
615 0           last SWITCH;};
616 0 0         $_ eq RDF_SEQ && do {$ct = NODE_SEQ;
  0            
617 0           $ce->{containertype} = RDFT_SEQ;
618 0           last SWITCH;};
619 0 0         $_ eq RDF_ALT && do {$ct = NODE_ALT;
  0            
620 0           $ce->{containertype} = RDFT_ALT;
621 0           last SWITCH;};
622             #deafult
623 0           $ct = NODE_UNKNOWN; #for now - property or typed object
624             }
625              
626             #check validity in the context of the parent node
627             #and optionally fix the node type for NODE_UNKNOWN
628 0           my $pe = $$path[-2];
629 0 0         my $pt = $pe ? $pe->{type} : undef;
630              
631 0 0 0       if (! defined $pt) {
    0          
    0          
    0          
    0          
632             #the very beginning
633 0 0 0       $expat->xpcroak("bad root element")
634             unless ($ct & NODE_OBJ) || $ct ==NODE_RDF;
635 0 0         if ($ct == NODE_UNKNOWN) {
636 0           $ct = NODE_TYPED;
637             }
638             }
639             elsif ($pt == NODE_RDF) {
640 0 0         $ct = NODE_TYPED if $ct == NODE_UNKNOWN;
641 0 0         $expat->xpcroak("invalid first level element") unless $ct & NODE_OBJ;
642             }
643             elsif ($pt == NODE_DESCRIPTION || $pt == NODE_TYPED) {
644 0 0         $expat->xpcroak("invalid node in the Description element")
645             unless $ct == NODE_UNKNOWN;
646             #force type to property - checked later
647 0           $ct = NODE_PROPERTY;
648             }
649             elsif ($pt & NODE_CONTAINER) {
650 0 0         $expat->xpcroak("invalid node in container")
651             unless $ct == NODE_UNKNOWN;
652 0           $ct = NODE_PROPERTY;
653             }
654             elsif ($pt & NODE_PROPERTY_MASK) {
655 0 0 0       $expat->xpcroak("invalid node in the memeber element") unless
656             $ct == NODE_UNKNOWN || ($ct & NODE_OBJ);
657 0 0         if ($ct == NODE_UNKNOWN) {
658 0 0         if ($pt == NODE_PROPERTY_3) {
659 0           $ct = NODE_PROPERTY;
660             } else {
661 0           $ct = NODE_TYPED;
662             }
663             }
664             }
665             else {
666 0           $expat->xpcroak("unknown parent node type: $pt");
667             }
668              
669             #if we found, that we're NODE_PROPERTY, we'' try to determine the subtype
670 0 0         if ($ct == NODE_PROPERTY) {
671 0           my $ruri = $self->_getElementResource($ce);
672 0 0 0       if ($ruri || %$attrs) {
    0          
673 0           $ct = NODE_PROPERTY_4;
674 0   0       $ce->{resource} ||= $self->_getImplicitURI($ce->{nodeid});
675             } elsif ($ce->{parsetype}) {
676 0 0         if ($ce->{parsetype} eq PARSE_COLLECTION) {
    0          
    0          
677 0           $ct = NODE_PROPERTY_5;
678             } elsif ($ce->{parsetype} eq PARSE_RESOURCE) {
679 0           $ct = NODE_PROPERTY_3;
680             } elsif ($ce->{parsetype} eq PARSE_LITERAL) {
681 0           $ct = NODE_PROPERTY_2;
682             }
683             } else {
684 0           $ct = NODE_PROPERTY_1;
685             }
686             }
687             #set node type
688 0           $ce->{type} = $ct;
689             }
690              
691             sub __slice {
692 0     0     my ($element, $keys) = @_;
693 0           my (%set, $count);
694 0           foreach (@$keys) {
695 0           $set{$_} = $element->{$_};
696 0 0         $count++ if defined $set{$_};
697             }
698 0 0         return wantarray ? %set : $count;
699             }
700              
701             sub _checkAttributes {
702 0     0     my ($self, $expat, $element, $attrs) = @_;
703              
704 0           my $allset = [qw(about abouteach id bagid parsetype rdftype resource
705             nodeid datatype)];
706 0           my $aboutset = [qw(about abouteach id)];
707             #all except about and bag
708 0           my $inverseset1 = [qw(parsetype resource datatype)];
709             #2 - all except id
710 0           my $inverseset2 = [qw(about abouteach bagid parsetype rdftype resource nodeid)];
711             #3 - all except id and parsetype
712 0           my $inverseset3 = [qw(about abouteach bagid rdftype resource nodeid datatype)];
713             #4 - all except resource, id and bag
714 0           my $inverseset4 = [qw(about abouteach parsetype rdftype datatype)];
715              
716 0           my $et = $element->{type};
717              
718             #check xml attributes (shouldn't be any)
719 0 0         if (grep {$_->{ns} eq XML_NS} values %$attrs) {
  0            
720 0           $expat->xpcroak("invalid xml attribute");
721             }
722 0 0 0       if ($et == NODE_RDF) {
    0 0        
    0 0        
    0          
    0          
    0          
723 0 0 0       $expat->xpcroak("invalid attribute")
724             if scalar __slice($element, $allset) || %$attrs;
725             }
726             elsif ($et == NODE_DESCRIPTION || $et == NODE_TYPED) {
727 0 0         $expat->xpcroak("invalid attribute")
728             if scalar __slice($element, $inverseset1);
729 0 0 0       $expat->xpcroak("invalid attribute")
730             if scalar __slice($element, $aboutset) && $element->{nodeid};
731             }
732             elsif ($et & NODE_CONTAINER) {
733 0 0         $expat->xpcroak("invalid attribute")
734             if scalar __slice($element, $inverseset1);
735 0 0         $element->{hasmembers} = 1 if %$attrs;
736             }
737             elsif ($et == NODE_PROPERTY_1) {
738 0 0 0       $expat->xpcroak("invalid attribute")
739             if scalar __slice($element, $inverseset2) || %$attrs;
740             }
741             elsif ($et == NODE_PROPERTY_2 || $et == NODE_PROPERTY_3
742             || $et == NODE_PROPERTY_5) {
743 0 0 0       $expat->xpcroak("invalid attribute")
744             if scalar __slice($element, $inverseset3) || %$attrs;
745             }
746             elsif ($et == NODE_PROPERTY_4) {
747 0 0         $expat->xpcroak("invalid attribute")
748             if scalar __slice($element, $inverseset4);
749 0 0         $element->{hasprops} = 1 if %$attrs;
750             }
751             }
752              
753             sub _checkNoResource {
754 0     0     my ($self, $expat, $element) = @_;
755 0 0         $expat->xpcroak("element contain both of rdf:resource and nested node")
756             if $element->{resource};
757 0 0         $expat->xpcroak("element contain both of rdf:nodeID and nested node")
758             if $element->{nodeid};
759             }
760              
761             #creates the 'current' subject
762             sub _createSubject {
763 0     0     my ($self, $expat, $element) = @_;
764 0           my $type = $element->{type};
765 0 0         if ($type & NODE_OBJ) {
    0          
766 0           push @{$self->{subjects}}, $element;
  0            
767 0           $element->{subject} = 1;
768 0           my $parent = ${$self->{path}}[-2];
  0            
769 0 0 0       if ($parent && $parent->{type} != NODE_RDF) {
770 0           $self->_checkNoResource($expat, $parent);
771 0           $parent->{rnode} = $element;
772             }
773             }
774             elsif ($type == NODE_PROPERTY_3) {
775             #rdf:parseType="Resource"
776 0           my $subject = {uri => $self->_getImplicitURI};
777 0           push @{$self->{subjects}}, $subject;
  0            
778 0           $element->{presubject} = 1;
779 0           $self->_checkNoResource($element);
780 0           $element->{rnode} = $subject;
781             }
782             }
783              
784             ########################################
785             # handlers
786             sub init {
787 0     0     my ($self, $expat) = @_;
788             # print "---> init\n";
789 0           $self->{path} = [];
790 0           $self->{subjects} = [];
791 0           $self->{status} = F_IDLE;
792 0           $self->{unique} = 0;
793 0           $self->{urimembers} = {};
794 0           $self->{abouteach} = {};
795             }
796              
797             sub final {
798 0     0     my ($self, $expat) = @_;
799             # print "---> final\n";
800 0           $self->_assertAboutEach;
801             # print "subjects: ", Dumper($self->{subjects});
802             # print "urimembers: ", Dumper($self->{urimembers});
803             # print "abouteachs: ", Dumper($self->{abouteach});
804             }
805              
806             sub start {
807 0     0     my ($self, $expat, $name, %attrs) = @_;
808 0           my $element;
809             my $subject;
810              
811             # extract namespace declarations and create element
812 0           push @{$self->{path}},
  0            
813             $element = { name => $self->_localName($name),
814             nslist => $self->_getNamespaces(\%attrs),
815             members => [],
816             };
817 0           $element->{ns} = $self->_findNS($self->_nsAbbr($name));
818 0           $element->{qname} = $element->{ns} . $element->{name};
819              
820             #expand attributes (must follow namespaces handling)
821 0           my $attrs = $self->_expandAttributes($element, %attrs);
822              
823             #update element (rename, read red attributes)
824 0           $self->_updateElement($element, $attrs);
825              
826             #now we have all (almost) information to decide on node type
827             #we must check the validity and update element status
828 0           $self->_analyzePath($expat, $attrs);
829              
830             #check whether attributes match the node type
831 0           $self->_checkAttributes($expat, $element, $attrs);
832              
833             # tool variables
834             #c'on baby...
835 0           $self->_createSubject($expat, $element);
836            
837             #spit out attributes
838 0           $self->_assertPropAttrs($element, $attrs);
839 0           $self->_assertRDFAttrs($element, $attrs);
840              
841             #switch to the literal mode if needed
842 0 0         if ($element->{type} == NODE_PROPERTY_2) {
843 0           $element->{datatype} = RDF_XMLLITERAL;
844              
845 0           $expat->setHandlers(%{$self->_getHandlersLiteral($name)});
  0            
846             }
847             }
848              
849             sub end {
850 0     0     my ($self, $expat, $name) = @_;
851 0           my $element = pop @{$self->{path}};
  0            
852            
853 0 0         pop @{$self->{subjects}} if $element->{presubject};
  0            
854              
855             #remember current subject
856 0           my $subject = $self->{subjects}[-1];
857              
858 0 0         if (($element->{type} & NODE_PROPERTY_MASK)) {
859 0           $self->_assertElement($expat, $subject, $element);
860             #update parent type (usefull for containers)
861 0 0         if ($element->{qname} eq RDF_TYPE) {
862 0           my $ctype = $RDF_TYPES{$self->_getElementResource($element)};
863 0 0 0       if ($ctype and $ctype & RDFT_CONTAINER) {
864 0           $subject->{containertype} = $ctype;
865             }
866             }
867             #catch memebers for aboutEach on ID
868 0           my $re = '^' . RDF_NS . '_\d+$';
869 0 0         if ($element->{qname} =~ /$re/) {
870 0           my $uri = $self->_getElementResource($element);
871 0 0         push @{$subject->{members}}, $uri if $uri;
  0            
872             }
873             }
874 0 0         if ($element->{subject}) {
875              
876             #Collection item
877 0 0 0       if ($self->{path}[-1] &&
878             $self->{path}[-1]->{type} == NODE_PROPERTY_5) {
879 0           $self->_assertCollectionItem($self->{subjects}[-2],
880             $self->{path}[-1], $element );
881             }
882             #remember aboutEach stuff
883 0 0         if ( $element->{containertype}) {
884 0           $self->{urimembers}{$element->{uri}} = $element->{members};
885             }
886 0 0         if ($element->{bagid}) {
887             #assert bags created by rdf:bagID attr
888 0           my $bagElement = {uri=>$element->{baguri}};
889 0           $self->_assertRDFType($bagElement,NODE_BAG);
890 0           foreach (@{$element->{bagmembers}}) {
  0            
891 0           my ($ns, $name, $uri) = $self->_getLIURI($bagElement);
892 0           my %params = (
893             subject_uri => $self->_uri($bagElement),
894             predicate_ns => $ns,
895             predicate_name => $name,
896             predicate_uri => $uri,
897             object_uri => $_,
898             );
899 0           $self->_doAssert($bagElement,\%params);
900             }
901             }
902             }
903              
904 0 0         pop @{$self->{subjects}} if $element->{subject};
  0            
905             }
906              
907             sub char {
908 0     0     my ($self, $expat, $string) = @_;
909 0           my $elt = ${$self->{path}}[-1];
  0            
910 0 0 0       if ($string !~ /^\s*$/s && $elt->{type} != NODE_PROPERTY_1) {
911 0           $expat->xpcroak("Element '" . $elt->{name} .
912             "' can not contain a literal value");
913             }
914 0           $elt->{text} .= $string;
915             }
916              
917             sub start_literal {
918 0     0     my ($self, $expat, $name, %attrs) = @_;
919 0           ${$self->{path}}[-1]{text} .= $expat->recognized_string;
  0            
920             }
921              
922             sub end_literal {
923 0     0     my ($self, $fname, $expat, $name) = @_;
924 0 0         if ($name eq $fname) {
925 0           $expat->setHandlers(%{$self->_getHandlersRegular});
  0            
926 0           $self->end($expat, $name);
927             } else {
928 0           ${$self->{path}}[-1]{text} .= $expat->recognized_string;
  0            
929             }
930             }
931              
932             sub char_literal {
933 0     0     my ($self, $expat, $string) = @_;
934 0           ${$self->{path}}[-1]{text} .= $string;
  0            
935             }
936              
937             ########################################
938             # private methods
939             sub _findNS {
940 0     0     my ($self, $abbr) = @_;
941 0 0         return XML_NS if $abbr eq 'xml';
942 0           foreach my $element (reverse @{$self->{path}}) {
  0            
943 0 0         return $element->{nslist}{$abbr} if exists $element->{nslist}{$abbr};
944             }
945             }
946             sub _findBaseURI {
947 0     0     my ($self) = @_;
948 0           my $baseURI = $self->{baseuri};
949 0           foreach my $element (reverse @{$self->{path}}) {
  0            
950 0 0         if (defined $element->{baseuri}) {
951 0           $baseURI = $element->{baseuri};
952 0           last;
953             }
954             }
955 0           return $baseURI;
956             }
957              
958             sub _findLang {
959 0     0     my ($self, $lastElement) = @_;
960 0           my $lang = $self->{lang};
961 0           foreach my $element ($lastElement, reverse @{$self->{path}}) {
  0            
962 0 0         next unless defined $element;
963 0 0         if (defined $element->{lang}) {
964 0           $lang = $element->{lang};
965 0           last;
966             }
967             }
968 0           return $lang;
969             }
970              
971             sub _getHandlers {
972 0     0     my $self = shift;
973 0     0     my %handlers = (
974             Init => sub {$self->init(@_)},
975 0     0     Final => sub {$self->final(@_)},
976 0     0     Start => sub {$self->start(@_)},
977 0     0     End => sub {$self->end(@_)},
978 0     0     Char => sub {$self->char(@_)},
979 0           );
980 0           return \%handlers;
981             }
982              
983             sub _getHandlersLiteral {
984 0     0     my ($self, $name) = @_;
985 0     0     my %handlers = (
986             Start => sub {$self->start_literal(@_)},
987 0     0     End => sub {$self->end_literal($name, @_)},
988 0     0     Char => sub {$self->char_literal(@_)},
989 0           );
990 0           return \%handlers;
991             }
992              
993             sub _getHandlersRegular {
994 0     0     my ($self, $name) = @_;
995 0     0     my %handlers = (
996             Start => sub {$self->start(@_)},
997 0     0     End => sub {$self->end(@_)},
998 0     0     Char => sub {$self->char(@_)},
999 0           );
1000 0           return \%handlers;
1001             }
1002              
1003             sub _createExpatParser {
1004 0     0     my $self = shift;
1005 0           my $expat = new XML::Parser(
1006             Handlers => $self->_getHandlers,
1007             );
1008 0           return $expat;
1009             }
1010              
1011             sub _setFlag {
1012 0     0     my ($self, $flag) = @_;
1013 0           $self->{status} |= $flag;
1014             }
1015              
1016             sub _hasFlag {
1017 0     0     my ($self, $flag) = @_;
1018 0           return $self->{status} & $flag;
1019             }
1020              
1021             sub _clearFlag {
1022 0     0     my ($self, $flag) = @_;
1023 0           $self->{status} &= ~$flag;
1024             }
1025              
1026             sub _getImplicitURI {
1027 0     0     my ($self, $nodeID) = @_;
1028 0           my $ret;
1029 0   0       $ret = "_:" .($self->{bnode} || "a");
1030 0           $ret .= ++ $self->{unique};
1031              
1032 0 0         if ($nodeID) {
1033 0 0         if ($self->{nodeid}{$nodeID}) {
1034             #use known node ID instead
1035 0           $ret = $self->{nodeid}{$nodeID}
1036             } else {
1037             #remember node ID
1038 0           $self->{nodeid}{$nodeID} = $ret
1039             }
1040             }
1041 0           return $ret;
1042             }
1043              
1044             1;
1045              
1046             __END__