/[suikacvs]/markup/html/whatpm/Whatpm/RDFXML.pm
Suika

Contents of /markup/html/whatpm/Whatpm/RDFXML.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (hide annotations) (download)
Fri Mar 21 11:17:32 2008 UTC (17 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.5: +28 -26 lines
++ whatpm/Whatpm/ChangeLog	21 Mar 2008 11:17:27 -0000
	* RDFXML.pm: Factored out ID checking code.

2008-03-21  Wakaba  <wakaba@suika.fam.cx>

1 wakaba 1.1 package Whatpm::RDFXML;
2     use strict;
3    
4     ## NOTE: EntityReference nodes are not supported except that
5     ## unexpected entity reference are simply ignored. (In fact
6     ## all EntityRefernce nodes are ignored.)
7    
8 wakaba 1.2 ## TODO: Add a callback function invoked for every element
9     ## when XMLCC is implemented in WDCC.
10    
11     ## ISSUE: <html:nest/> in RDF subtree?
12    
13     ## ISSUE: PIs in RDF subtree should be validated?
14    
15 wakaba 1.5 ## TODO: Should we validate expanded URI created from QName?
16    
17     ## TODO: attributes in null namespace
18    
19     ## TODO: elements in null namespace (not mentioned in the spec.)
20    
21 wakaba 1.1 my $RDF_URI = q<http://www.w3.org/1999/02/22-rdf-syntax-ns#>;
22    
23 wakaba 1.5 use Char::Class::XML qw(InXML_NCNameStartChar10 InXMLNameChar10);
24     require Whatpm::URIChecker;
25    
26 wakaba 1.1 sub new ($) {
27 wakaba 1.5 my $self = bless {fact_level => 'm', grammer_level => 'm',
28     info_level => 'i', next_id => 0}, shift;
29 wakaba 1.1 $self->{onerror} = sub {
30     my %opt = @_;
31     warn $opt{type}, "\n";
32     };
33     $self->{ontriple} = sub {
34     my %opt = @_;
35     my $dump_resource = sub {
36     my $resource = shift;
37 wakaba 1.3 if (defined $resource->{uri}) {
38 wakaba 1.1 return '<' . $resource->{uri} . '>';
39 wakaba 1.3 } elsif (defined $resource->{bnodeid}) {
40 wakaba 1.1 return '_:' . $resource->{bnodeid};
41     } elsif ($resource->{nodes}) {
42     return '"' . join ('', map {$_->inner_html} @{$resource->{nodes}}) .
43     '"^^<' . $resource->{datatype} . '>';
44     } elsif (defined $resource->{value}) {
45     return '"' . $resource->{value} . '"' .
46     (defined $resource->{datatype}
47     ? '^^<' . $resource->{datatype} . '>'
48     : '@' . $resource->{language});
49     } else {
50     return '??';
51     }
52     };
53     print STDERR $dump_resource->($opt{subject}) . ' ';
54     print STDERR $dump_resource->($opt{predicate}) . ' ';
55     print STDERR $dump_resource->($opt{object}) . "\n";
56 wakaba 1.3 if ($dump_resource->{id}) {
57     print STDERR $dump_resource->($dump_resource->{id}) . ' ';
58     print STDERR $dump_resource->({uri => $RDF_URI . 'subject'}) . ' ';
59     print STDERR $dump_resource->($opt{subject}) . "\n";
60     print STDERR $dump_resource->($dump_resource->{id}) . ' ';
61     print STDERR $dump_resource->({uri => $RDF_URI . 'predicate'}) . ' ';
62     print STDERR $dump_resource->($opt{predicate}) . "\n";
63     print STDERR $dump_resource->($dump_resource->{id}) . ' ';
64     print STDERR $dump_resource->({uri => $RDF_URI . 'object'}) . ' ';
65     print STDERR $dump_resource->($opt{object}) . "\n";
66     print STDERR $dump_resource->($dump_resource->{id}) . ' ';
67     print STDERR $dump_resource->({uri => $RDF_URI . 'type'}) . ' ';
68     print STDERR $dump_resource->({uri => $RDF_URI . 'Statement'}) . "\n";
69     }
70 wakaba 1.1 };
71     return $self;
72     } # new
73    
74     sub convert_document ($$) {
75     my $self = shift;
76     my $node = shift; # Document
77    
78     ## ISSUE: An RDF/XML document, either |doc| or |nodeElement|
79     ## is allowed as a starting production. However, |nodeElement|
80     ## is not a Root Event.
81    
82     my $has_element;
83    
84     for my $cn (@{$node->child_nodes}) {
85     if ($cn->node_type == $cn->ELEMENT_NODE) {
86     unless ($has_element) {
87     if ($cn->manakai_expanded_uri eq $RDF_URI . q<RDF>) {
88     $self->convert_rdf_element ($cn);
89     } else {
90     $self->convert_rdf_node_element ($cn);
91     }
92     $has_element = 1;
93     } else {
94     $self->{onerror}->(type => 'second node element',
95     level => $self->{grammer_level},
96     node => $cn);
97     }
98     } elsif ($cn->node_type == $cn->TEXT_NODE or
99     $cn->node_type == $cn->CDATA_SECTION_NODE) {
100     $self->{onerror}->(type => 'character not allowed',
101     level => $self->{grammer_level},
102     node => $cn);
103     }
104     }
105     } # convert_document
106    
107     my $check_rdf_namespace = sub {
108     my $self = shift;
109     my $node = shift;
110     my $node_nsuri = $node->namespace_uri;
111     return unless defined $node_nsuri;
112     if (substr ($node_nsuri, 0, length $RDF_URI) eq $RDF_URI and
113     length $RDF_URI < length $node_nsuri) {
114     $self->{onerror}->(type => 'bad rdf namespace',
115     level => $self->{fact_level}, # Section 5.1
116     node => $node);
117     }
118     }; # $check_rdf_namespace
119    
120     sub convert_rdf_element ($$) {
121     my ($self, $node) = @_;
122    
123     $check_rdf_namespace->($self, $node);
124    
125     # |RDF|
126    
127     for my $attr (@{$node->attributes}) {
128     my $prefix = $attr->prefix;
129     if (defined $prefix) {
130     next if $prefix =~ /^[Xx][Mm][Ll]/;
131     } else {
132     next if $attr->manakai_local_name =~ /^[Xx][Mm][Ll]/;
133     }
134    
135     $check_rdf_namespace->($self, $attr);
136     $self->{onerror}->(type => 'attribute not allowed',
137     level => $self->{grammer_level},
138     node => $attr);
139     }
140    
141     # |nodeElementList|
142     for my $cn (@{$node->child_nodes}) {
143     if ($cn->node_type == $cn->ELEMENT_NODE) {
144     $self->convert_node_element ($cn);
145     } elsif ($cn->node_type == $cn->TEXT_NODE or
146     $cn->node_type == $cn->CDATA_SECTION_NODE) {
147     if ($cn->data =~ /[^\x09\x0A\x0D\x20]/) {
148     $self->{onerror}->(type => 'character not allowed',
149     level => $self->{grammer_level},
150     node => $cn);
151     }
152     }
153     }
154     } # convert_rdf_element
155    
156     my %coreSyntaxTerms = (
157     $RDF_URI . 'RDF' => 1,
158     $RDF_URI . 'ID' => 1,
159     $RDF_URI . 'about' => 1,
160     $RDF_URI . 'parseType' => 1,
161     $RDF_URI . 'resource' => 1,
162     $RDF_URI . 'nodeID' => 1,
163     $RDF_URI . 'datatype' => 1,
164     );
165    
166     my %oldTerms = (
167     $RDF_URI . 'aboutEach' => 1,
168     $RDF_URI . 'aboutEachPrefix' => 1,
169     $RDF_URI . 'bagID' => 1,
170     );
171    
172 wakaba 1.4 require Message::DOM::DOMImplementation;
173     my $resolve = sub {
174     return Message::DOM::DOMImplementation->create_uri_reference ($_[0])
175     ->get_absolute_reference ($_[1]->base_uri)
176     ->uri_reference;
177    
178 wakaba 1.5 ## TODO: Ummm... RDF/XML spec refers dated version of xml:base and RFC 2396...
179    
180 wakaba 1.4 ## TODO: Check latest xml:base and IRI spec...
181     ## (non IRI/URI chars should be percent-encoded before resolve?)
182     }; # $resolve
183    
184     my $generate_bnodeid = sub {
185     return 'g'.$_[0]->{next_id}++;
186     }; # $generate_bnodeid
187    
188     my $get_bnodeid = sub {
189     return 'b'.$_[0];
190     }; # $get_bnodeid
191    
192 wakaba 1.5 my $uri_attr = sub {
193     my ($self, $attr) = @_;
194    
195     my $abs_uri = $resolve->($attr->value, $attr);
196    
197     Whatpm::URIChecker->check_iri_reference ($abs_uri, sub {
198     my %opt = @_;
199     $self->{onerror}->(node => $attr, level => $opt{level},
200     type => 'URI::'.$opt{type}.
201     (defined $opt{position} ? ':'.$opt{position} : ''));
202     });
203    
204     return $abs_uri;
205 wakaba 1.6 }; # $uri_attr
206    
207     my $id_attr = sub {
208     my ($self, $attr) = @_;
209    
210     my $id = $attr->value;
211     unless ($id =~ /\A\p{InXML_NCNameStartChar10}\p{InXMLNCNameChar10}*\z/) {
212     $self->{onerror}->(type => 'syntax error', ## TODO: type
213     level => $self->{grammer_level},
214     node => $attr);
215     }
216    
217     return $resolve->('#' . $id, $attr);
218 wakaba 1.5
219     ## TODO: rdf:ID, base-uri pair must (lowercase) be unique in the document.
220 wakaba 1.6 }; # $id_attr
221 wakaba 1.5
222 wakaba 1.1 sub convert_node_element ($$) {
223     my ($self, $node) = @_;
224    
225     $check_rdf_namespace->($self, $node);
226    
227     # |nodeElement|
228    
229     my $xuri = $node->manakai_expanded_uri;
230    
231     if ({
232     %coreSyntaxTerms,
233     $RDF_URI . 'li' => 1,
234     %oldTerms,
235     }->{$xuri}) {
236     $self->{onerror}->(type => 'element not allowed',
237     level => $self->{grammer_level},
238     node => $node);
239    
240     ## TODO: W3C RDF Validator: Continue validation, but triples that would
241     ## be generated from the subtree are ignored.
242     }
243    
244     my $subject;
245 wakaba 1.4 my $type_attr;
246 wakaba 1.1 my @prop_attr;
247    
248     for my $attr (@{$node->attributes}) {
249     my $prefix = $attr->prefix;
250     if (defined $prefix) {
251     next if $prefix =~ /^[Xx][Mm][Ll]/;
252     } else {
253     next if $attr->manakai_local_name =~ /^[Xx][Mm][Ll]/;
254     }
255    
256     $check_rdf_namespace->($self, $attr);
257    
258     my $attr_xuri = $attr->manakai_expanded_uri;
259 wakaba 1.2 if ($attr_xuri eq $RDF_URI . 'ID') {
260 wakaba 1.1 unless (defined $subject) {
261 wakaba 1.6 $subject = {uri => $id_attr->($self, $attr)};
262 wakaba 1.1 } else {
263 wakaba 1.5 $self->{onerror}->(type => 'attribute not allowed',
264     level => $self->{grammer_level},
265     node => $attr);
266    
267 wakaba 1.1 ## TODO: Ignore triple as W3C RDF Validator does
268     }
269     } elsif ($attr_xuri eq $RDF_URI . 'nodeID') {
270     unless (defined $subject) {
271 wakaba 1.5 my $id = $attr->value;
272     unless ($id =~ /\A\p{InXML_NCNameStartChar10}\p{InXMLNCNameChar10}*\z/) {
273     $self->{onerror}->(type => 'syntax error', ## TODO: type
274     level => $self->{grammer_level},
275     node => $self);
276     }
277    
278     $subject = {bnodeid => $get_bnodeid->($id)};
279 wakaba 1.1 } else {
280 wakaba 1.5 $self->{onerror}->(type => 'attribute not allowed',
281     level => $self->{grammer_level},
282     node => $attr);
283    
284 wakaba 1.1 ## TODO: Ignore triple as W3C RDF Validator does
285     }
286     } elsif ($attr_xuri eq $RDF_URI . 'about') {
287     unless (defined $subject) {
288 wakaba 1.5 $subject = {uri => $uri_attr->($self, $attr)};
289 wakaba 1.1 } else {
290 wakaba 1.5 $self->{onerror}->(type => 'attribute not allowed',
291     level => $self->{grammer_level},
292     node => $attr);
293    
294 wakaba 1.1 ## TODO: Ignore triple as W3C RDF Validator does
295     }
296     } elsif ($attr_xuri eq $RDF_URI . 'type') {
297 wakaba 1.4 $type_attr = $attr;
298 wakaba 1.1 } elsif ({
299     %coreSyntaxTerms,
300     $RDF_URI . 'li' => 1,
301     $RDF_URI . 'Description' => 1,
302     %oldTerms,
303     }->{$attr_xuri}) {
304     $self->{onerror}->(type => 'attribute not allowed',
305     level => $self->{grammer_level},
306     node => $attr);
307    
308     ## TODO: W3C RDF Validator: Ignore triples
309     } else {
310     push @prop_attr, $attr;
311     }
312     }
313    
314     unless (defined $subject) {
315 wakaba 1.4 $subject = {bnodeid => $generate_bnodeid->($self)};
316 wakaba 1.1 }
317    
318     if ($xuri ne $RDF_URI . 'Description') {
319     $self->{ontriple}->(subject => $subject,
320     predicate => {uri => $RDF_URI . 'type'},
321 wakaba 1.2 object => {uri => $xuri},
322     node => $node);
323 wakaba 1.1 }
324    
325 wakaba 1.4 if ($type_attr) {
326 wakaba 1.1 $self->{ontriple}->(subject => $subject,
327     predicate => {uri => $RDF_URI . 'type'},
328 wakaba 1.4 object => {uri => $resolve->($type_attr->value,
329     $type_attr)},
330     node => $type_attr);
331 wakaba 1.1 }
332    
333     for my $attr (@prop_attr) {
334     $self->{ontriple}->(subject => $subject,
335     predicate => {uri => $attr->manakai_expanded_uri},
336 wakaba 1.2 object => {value => $attr->value}, ## TODO: language
337     node => $attr);
338 wakaba 1.1 ## TODO: SHOULD in NFC
339     }
340    
341     # |propertyEltList|
342    
343     my $li_counter = 1;
344     for my $cn (@{$node->child_nodes}) {
345     my $cn_type = $cn->node_type;
346     if ($cn_type == $cn->ELEMENT_NODE) {
347     $self->convert_property_element ($cn, li_counter => \$li_counter,
348     subject => $subject);
349     } elsif ($cn_type == $cn->TEXT_NODE or
350     $cn_type == $cn->CDATA_SECTION_NODE) {
351     if ($cn->data =~ /[^\x09\x0A\x0D\x20]/) {
352     $self->{onerror}->(type => 'character not allowed',
353     level => $self->{grammer_level},
354     node => $cn);
355     }
356     }
357     }
358    
359     return $subject;
360     } # convert_node_element
361    
362 wakaba 1.4 my $get_id_resource = sub {
363 wakaba 1.5 my $self = shift;
364     my $node = shift;
365 wakaba 1.6
366 wakaba 1.5 return undef unless $node;
367 wakaba 1.6
368     return {uri => $id_attr->($self, $node)};
369 wakaba 1.4 }; # $get_id_resource
370    
371 wakaba 1.1 sub convert_property_element ($$%) {
372     my ($self, $node, %opt) = @_;
373    
374     $check_rdf_namespace->($self, $node);
375    
376     # |propertyElt|
377    
378     my $xuri = $node->manakai_expanded_uri;
379     if ($xuri eq $RDF_URI . 'li') {
380     $xuri = $RDF_URI . '_' . ${$opt{li_counter}}++;
381     }
382    
383     if ({
384     %coreSyntaxTerms,
385     $RDF_URI . 'Description' => 1,
386     %oldTerms,
387     }->{$xuri}) {
388     $self->{onerror}->(type => 'element not allowed',
389     level => $self->{grammer_level},
390     node => $node);
391     ## TODO: RDF Validator?
392     }
393    
394 wakaba 1.6 my $rdf_id_attr;
395 wakaba 1.1 my $dt_attr;
396     my $parse_attr;
397     my $nodeid_attr;
398     my $resource_attr;
399     my @prop_attr;
400     for my $attr (@{$node->attributes}) {
401     my $prefix = $attr->prefix;
402     if (defined $prefix) {
403     next if $prefix =~ /^[Xx][Mm][Ll]/;
404     } else {
405     next if $attr->manakai_local_name =~ /^[Xx][Mm][Ll]/;
406     }
407    
408     $check_rdf_namespace->($self, $attr);
409    
410     my $attr_xuri = $attr->manakai_expanded_uri;
411     if ($attr_xuri eq $RDF_URI . 'ID') {
412 wakaba 1.6 $rdf_id_attr = $attr;
413 wakaba 1.1 } elsif ($attr_xuri eq $RDF_URI . 'datatype') {
414     $dt_attr = $attr;
415     } elsif ($attr_xuri eq $RDF_URI . 'parseType') {
416     $parse_attr = $attr;
417     } elsif ($attr_xuri eq $RDF_URI . 'resource') {
418     $resource_attr = $attr;
419     } elsif ($attr_xuri eq $RDF_URI . 'nodeID') {
420     $nodeid_attr = $attr;
421     } elsif ({
422     %coreSyntaxTerms,
423     $RDF_URI . 'li' => 1,
424     $RDF_URI . 'Description' => 1,
425     %oldTerms,
426     }->{$attr_xuri}) {
427     $self->{onerror}->(type => 'attribute not allowed',
428     level => $self->{grammer_level},
429     node => $attr);
430     ## TODO: RDF Validator?
431     } else {
432     push @prop_attr, $attr;
433     }
434     }
435    
436     my $parse = $parse_attr ? $parse_attr->value : '';
437     if ($parse eq 'Resource') {
438     # |parseTypeResourcePropertyElt|
439    
440     for my $attr ($resource_attr, $nodeid_attr, $dt_attr) {
441 wakaba 1.2 next unless $attr;
442 wakaba 1.1 $self->{onerror}->(type => 'attribute not allowed',
443     level => $self->{grammer_level},
444     node => $attr);
445     ## TODO: RDF Validator?
446     }
447    
448 wakaba 1.4 my $object = {bnodeid => $generate_bnodeid->($self)};
449 wakaba 1.1 $self->{ontriple}->(subject => $opt{subject},
450     predicate => {uri => $xuri},
451 wakaba 1.2 object => $object,
452 wakaba 1.3 node => $node,
453 wakaba 1.6 id => $get_id_resource->($self, $rdf_id_attr));
454 wakaba 1.1
455     ## As if nodeElement
456    
457     # |propertyEltList|
458    
459     my $li_counter = 1;
460     for my $cn (@{$node->child_nodes}) {
461     my $cn_type = $cn->node_type;
462     if ($cn_type == $cn->ELEMENT_NODE) {
463     $self->convert_property_element ($cn, li_counter => \$li_counter,
464     subject => $object);
465     } elsif ($cn_type == $cn->TEXT_NODE or
466     $cn_type == $cn->CDATA_SECTION_NODE) {
467     if ($cn->data =~ /[^\x09\x0A\x0D\x20]/) {
468     $self->{onerror}->(type => 'character not allowed',
469     level => $self->{grammer_level},
470     node => $cn);
471     }
472     }
473     }
474     } elsif ($parse eq 'Collection') {
475     # |parseTypeCollectionPropertyElt|
476    
477     for my $attr ($resource_attr, $nodeid_attr, $dt_attr) {
478 wakaba 1.2 next unless $attr;
479 wakaba 1.1 $self->{onerror}->(type => 'attribute not allowed',
480     level => $self->{grammer_level},
481     node => $attr);
482     ## TODO: RDF Validator?
483     }
484    
485     # |nodeElementList|
486     my @resource;
487     for my $cn (@{$node->child_nodes}) {
488     if ($cn->node_type == $cn->ELEMENT_NODE) {
489     push @resource, [$self->convert_node_element ($cn),
490 wakaba 1.4 {bnodeid => $generate_bnodeid->($self)},
491 wakaba 1.2 $cn];
492 wakaba 1.1 } elsif ($cn->node_type == $cn->TEXT_NODE or
493     $cn->node_type == $cn->CDATA_SECTION_NODE) {
494     if ($cn->data =~ /[^\x09\x0A\x0D\x20]/) {
495     $self->{onerror}->(type => 'character not allowed',
496     level => $self->{grammer_level},
497     node => $cn);
498     }
499     }
500     }
501    
502     if (@resource) {
503     $self->{ontriple}->(subject => $opt{subject},
504     predicate => {uri => $xuri},
505 wakaba 1.2 object => $resource[0]->[1],
506     node => $node);
507 wakaba 1.1 } else {
508     $self->{ontriple}->(subject => $opt{subject},
509     predicate => {uri => $xuri},
510 wakaba 1.2 object => {uri => $RDF_URI . 'nil'},
511 wakaba 1.3 node => $node,
512 wakaba 1.6 id => $get_id_resource->($self, $rdf_id_attr));
513 wakaba 1.1 }
514 wakaba 1.3
515 wakaba 1.1 while (@resource) {
516     my $resource = shift @resource;
517     $self->{ontriple}->(subject => $resource->[1],
518     predicate => {uri => $RDF_URI . 'first'},
519 wakaba 1.2 object => $resource->[0],
520     node => $resource->[2]);
521 wakaba 1.1 if (@resource) {
522     $self->{ontriple}->(subject => $resource->[1],
523     predicate => {uri => $RDF_URI . 'rest'},
524 wakaba 1.2 object => $resource[0]->[1],
525     node => $resource->[2]);
526 wakaba 1.1 } else {
527     $self->{ontriple}->(subject => $resource->[1],
528     predicate => {uri => $RDF_URI . 'rest'},
529 wakaba 1.2 object => {uri => $RDF_URI . 'nil'},
530     node => $resource->[2]);
531 wakaba 1.1 }
532     }
533     } elsif ($parse_attr) {
534     # |parseTypeLiteralPropertyElt|
535 wakaba 1.5
536     if ($parse ne 'Literal') {
537     # |parseTypeOtherPropertyElt| ## TODO: What RDF Validator does?
538    
539     $self->{onerror}->(type => 'parse type other',
540     level => $self->{info_level},
541     node => $parse_attr);
542     }
543 wakaba 1.1
544     for my $attr ($resource_attr, $nodeid_attr, $dt_attr) {
545 wakaba 1.2 next unless $attr;
546 wakaba 1.1 $self->{onerror}->(type => 'attribute not allowed',
547     level => $self->{grammer_level},
548     node => $attr);
549     ## TODO: RDF Validator?
550     }
551    
552     my $value = [@{$node->child_nodes}];
553     ## TODO: Callback for validation
554     ## TODO: Serialized form SHOULD be in NFC.
555    
556     $self->{ontriple}->(subject => $opt{subject},
557     predicate => {uri => $xuri},
558     object => {nodes => $value,
559 wakaba 1.2 datatype => $RDF_URI . 'XMLLiteral'},
560 wakaba 1.3 node => $node,
561 wakaba 1.6 id => $get_id_resource->($self, $rdf_id_attr));
562 wakaba 1.1 } else {
563     my $mode = 'unknown';
564    
565     if ($dt_attr) {
566     $mode = 'literal'; # |literalPropertyElt|
567     ## TODO: What RDF Validator does for |< rdf:datatype><el/></>|?
568     }
569     ## TODO: What RDF Validator does for |< prop-attr><non-empty/></>|?
570    
571     my $node_element;
572     my $text = '';
573     for my $cn (@{$node->child_nodes}) {
574     my $cn_type = $cn->node_type;
575     if ($cn_type == $cn->ELEMENT_NODE) {
576     unless ($node_element) {
577     $node_element = $cn;
578     if ({
579     resource => 1, unknown => 1, 'literal-or-resource' => 1,
580     }->{$mode}) {
581     $mode = 'resource';
582     } else {
583     $self->{onerror}->(type => 'element not allowed',
584     level => $self->{grammer_level},
585     node => $cn);
586     ## TODO: RDF Validator?
587     }
588     } else {
589     ## TODO: What RDF Validator does?
590     $self->{onerror}->(type => 'second node element',
591     level => $self->{grammer_level},
592     node => $cn);
593     }
594     } elsif ($cn_type == $cn->TEXT_NODE or
595     $cn_type == $cn->CDATA_SECTION_NODE) {
596     my $data = $cn->data;
597     $text .= $data;
598     if ($data =~ /[^\x09\x0A\x0D\x20]/) {
599     if ({
600     literal => 1, unknown => 1, 'literal-or-resource' => 1,
601     }->{$mode}) {
602     $mode = 'literal';
603     } else {
604     $self->{onerror}->(type => 'character not allowed',
605     level => $self->{grammer_level},
606     node => $cn);
607     ## TODO: RDF Validator?
608     }
609     } else {
610     if ($mode eq 'unknown') {
611     $mode = 'literal-or-resource';
612     } else {
613     #
614     }
615     }
616     }
617     }
618    
619     if ($mode eq 'resource') {
620     # |resourcePropertyElt|
621    
622     for my $attr (@prop_attr, $resource_attr, $nodeid_attr, $dt_attr) {
623 wakaba 1.2 next unless $attr;
624 wakaba 1.1 $self->{onerror}->(type => 'attribute not allowed',
625     level => $self->{grammer_level},
626     node => $attr);
627     ## TODO: RDF Validator?
628     }
629    
630     my $object = $self->convert_node_element ($node_element);
631    
632     $self->{ontriple}->(subject => $opt{subject},
633     predicate => {uri => $xuri},
634 wakaba 1.2 object => $object,
635 wakaba 1.3 node => $node,
636 wakaba 1.6 id => $get_id_resource->($self, $rdf_id_attr));
637 wakaba 1.1 } elsif ($mode eq 'literal' or $mode eq 'literal-or-resource') {
638     # |literalPropertyElt|
639    
640     for my $attr (@prop_attr, $resource_attr, $nodeid_attr) {
641 wakaba 1.2 next unless $attr;
642 wakaba 1.1 $self->{onerror}->(type => 'attribute not allowed',
643     level => $self->{grammer_level},
644     node => $attr);
645     ## TODO: RDF Validator?
646     }
647    
648     ## TODO: $text SHOULD be in NFC
649    
650     if ($dt_attr) {
651 wakaba 1.5 $self->{ontriple}
652     ->(subject => $opt{subject},
653     predicate => {uri => $xuri},
654     object => {value => $text,
655     datatype => $uri_attr->$self, ($dt_attr->value)},
656     ## ISSUE: No resolve() in the spec (but spec says that
657     ## xml:base is applied also to rdf:datatype).
658     node => $node,
659 wakaba 1.6 id => $get_id_resource->($self, $rdf_id_attr));
660 wakaba 1.1 } else {
661     $self->{ontriple}->(subject => $opt{subject},
662     predicate => {uri => $xuri},
663     object => {value => $text,
664     ## TODO: language
665 wakaba 1.2 },
666 wakaba 1.3 node => $node,
667 wakaba 1.6 id => $get_id_resource->($self, $rdf_id_attr));
668 wakaba 1.1 }
669     } else {
670     ## |emptyPropertyElt|
671    
672     for my $attr ($dt_attr) {
673 wakaba 1.2 next unless $attr;
674 wakaba 1.1 $self->{onerror}->(type => 'attribute not allowed',
675     level => $self->{grammer_level},
676     node => $attr);
677     ## TODO: RDF Validator?
678     }
679    
680     if (not $resource_attr and not $nodeid_attr and not @prop_attr) {
681     $self->{ontriple}->(subject => $opt{subject},
682     predicate => {uri => $xuri},
683     object => {value => '',
684     ## TODO: language
685 wakaba 1.2 },
686 wakaba 1.3 node => $node,
687 wakaba 1.6 id => $get_id_resource->($self, $rdf_id_attr));
688 wakaba 1.1 } else {
689     my $object;
690     if ($resource_attr) {
691 wakaba 1.5 $object = {uri => $uri_attr->($self, $resource_attr)};
692     if (defined $nodeid_attr) {
693     $self->{onerror}->(type => 'attribute not allowed',
694     level => $self->{grammer_level},
695     node => $nodeid_attr);
696     ## TODO: RDF Validator?
697     }
698 wakaba 1.1 } elsif ($nodeid_attr) {
699 wakaba 1.5 my $id = $nodeid_attr->value;
700     unless ($id =~ /\A\p{InXML_NCNameStartChar10}\p{InXMLNCNameChar10}*\z/) {
701     $self->{onerror}->(type => 'syntax error', ## TODO: type
702     level => $self->{grammer_level},
703     node => $self);
704     }
705     $object = {bnodeid => $get_bnodeid->($id)};
706 wakaba 1.1 } else {
707 wakaba 1.4 $object = {bnodeid => $generate_bnodeid->($self)};
708 wakaba 1.1 }
709    
710     for my $attr (@prop_attr) {
711     my $attr_xuri = $attr->manakai_expanded_uri;
712     if ($attr_xuri eq $RDF_URI . 'type') {
713     $self->{ontriple}->(subject => $object,
714     predicate => {uri => $attr_xuri},
715 wakaba 1.4 object => $resolve->($attr->value, $attr),
716 wakaba 1.2 node => $attr);
717 wakaba 1.1 } else {
718     ## TODO: SHOULD be in NFC
719     $self->{ontriple}->(subject => $object,
720     predicate => {uri => $attr_xuri},
721     object => {value => $attr->value,
722     ## TODO: lang
723 wakaba 1.2 },
724     node => $attr);
725 wakaba 1.1 }
726     }
727    
728     $self->{ontriple}->(subject => $opt{subject},
729     predicate => {uri => $xuri},
730 wakaba 1.2 object => $object,
731 wakaba 1.3 node => $node,
732 wakaba 1.6 id => $get_id_resource->($self, $rdf_id_attr));
733 wakaba 1.1 }
734     }
735     }
736     } # convert_property_element
737    
738     1;
739    

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24