/[suikacvs]/markup/html/whatpm/Whatpm/ContentChecker/Atom.pm
Suika

Contents of /markup/html/whatpm/Whatpm/ContentChecker/Atom.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.5 - (hide annotations) (download)
Sat Sep 29 04:45:10 2007 UTC (17 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.4: +1 -0 lines
++ whatpm/t/ChangeLog	29 Sep 2007 04:36:22 -0000
2007-09-29  Wakaba  <wakaba@suika.fam.cx>

	* tokenizer-test-1.test: New tests for invalid
	attribute specifications are added.

++ whatpm/Whatpm/ChangeLog	29 Sep 2007 04:38:17 -0000
	* ContentChecker.pm: Raise specific error for invalid
	root element.

	* SelectorsParser.pm: Pass an empty string as a prefix
	for lookup namespace prefix callback, for loose compatibility
	with the |NSResolver| interface.

2007-09-24  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ContentChecker/ChangeLog	29 Sep 2007 04:38:46 -0000
	* Atom.pm (atom:link@title): Definition was missing.

2007-09-24  Wakaba  <wakaba@suika.fam.cx>

1 wakaba 1.1 package Whatpm::ContentChecker;
2     use strict;
3     require Whatpm::ContentChecker;
4    
5 wakaba 1.2 require Whatpm::URIChecker;
6    
7 wakaba 1.1 my $ATOM_NS = q<http://www.w3.org/2005/Atom>;
8 wakaba 1.3 my $LINK_REL = q<http://www.iana.org/assignments/relation/>;
9 wakaba 1.1
10     ## MUST be well-formed XML (RFC 4287 references XML 1.0 REC 20040204)
11    
12     ## NOTE: Commants and PIs are not explicitly allowed.
13    
14     our $AttrChecker;
15    
16     ## Any element MAY have xml:base, xml:lang
17     my $GetAtomAttrsChecker = sub {
18     my $element_specific_checker = shift;
19     return sub {
20     my ($self, $todo) = @_;
21     for my $attr (@{$todo->{node}->attributes}) {
22     my $attr_ns = $attr->namespace_uri;
23     $attr_ns = '' unless defined $attr_ns;
24     my $attr_ln = $attr->manakai_local_name;
25     my $checker;
26     if ($attr_ns eq '') {
27     $checker = $element_specific_checker->{$attr_ln};
28     } else {
29     $checker = $AttrChecker->{$attr_ns}->{$attr_ln}
30     || $AttrChecker->{$attr_ns}->{''};
31     }
32     if ($checker) {
33     $checker->($self, $attr, $todo);
34     } else {
35     $self->{onerror}->(node => $attr, level => 'unsupported',
36     type => 'attribute');
37     ## ISSUE: No comformance createria for unknown attributes in the spec
38     }
39     }
40     };
41     }; # $GetAtomAttrsChecker
42    
43 wakaba 1.4 my $AtomLanguageTagAttrChecker = sub {
44     ## NOTE: See also $HTMLLanguageTagAttrChecker in HTML.pm.
45    
46     my ($self, $attr) = @_;
47     my $value = $attr->value;
48     require Whatpm::LangTag;
49     Whatpm::LangTag->check_rfc3066_language_tag ($value, sub {
50     my %opt = @_;
51     my $type = 'LangTag:'.$opt{type};
52     $type .= ':' . $opt{subtag} if defined $opt{subtag};
53     $self->{onerror}->(node => $attr, type => $type, value => $opt{value},
54     level => $opt{level});
55     });
56     ## ISSUE: RFC 4646 (3066bis)?
57     }; # $AtomLanguageTagAttrChecker
58    
59 wakaba 1.1 my $AtomTextConstruct = {
60     attrs_checker => $GetAtomAttrsChecker->({
61     type => sub { 1 }, # checked in |checker|
62     }),
63     checker => sub {
64     my ($self, $todo) = @_;
65    
66     my $attr = $todo->{node}->get_attribute_node_ns (undef, 'type');
67     my $value = 'text';
68     if ($attr) {
69     $value = $attr->value;
70     if ($value eq 'text' or $value eq 'html' or $value eq 'xhtml') {
71     # MUST
72     } else {
73     $self->{onerror}->(node => $attr, type => 'keyword:invalid');
74     }
75     # IMT MUST NOT be used
76     }
77    
78     if ($value eq 'text') {
79     my @nodes = (@{$todo->{node}->child_nodes});
80     my $new_todos = [];
81    
82     while (@nodes) {
83     my $node = shift @nodes;
84     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
85    
86     my $nt = $node->node_type;
87     if ($nt == 1) {
88     # MUST NOT
89     $self->{onerror}->(node => $node, type => 'element not allowed');
90     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
91     unshift @nodes, @$sib;
92     push @$new_todos, @$ch;
93     } elsif ($nt == 5) {
94     unshift @nodes, @{$node->child_nodes};
95     }
96     }
97    
98     return ($new_todos);
99     } elsif ($value eq 'html') {
100     my @nodes = (@{$todo->{node}->child_nodes});
101     my $new_todos = [];
102    
103     while (@nodes) {
104     my $node = shift @nodes;
105     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
106    
107     my $nt = $node->node_type;
108     if ($nt == 1) {
109     # MUST NOT
110     $self->{onerror}->(node => $node, type => 'element not allowed');
111     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
112     unshift @nodes, @$sib;
113     push @$new_todos, @$ch;
114     } elsif ($nt == 5) {
115     unshift @nodes, @{$node->child_nodes};
116     }
117     }
118    
119     ## TODO: SHOULD be suitable for handling as HTML [HTML4]
120     # markup MUST be escaped
121     ## TODO: HTML SHOULD be valid as if within <div>
122    
123     return ($new_todos);
124     } elsif ($value eq 'xhtml') {
125     my @nodes = (@{$todo->{node}->child_nodes});
126     my $new_todos = [];
127    
128     my $has_div;
129     while (@nodes) {
130     my $node = shift @nodes;
131     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
132    
133     my $nt = $node->node_type;
134     if ($nt == 1) {
135     # MUST
136     my $nsuri = $node->namespace_uri;
137     if (defined $nsuri and
138     $nsuri eq q<http://www.w3.org/1999/xhtml> and
139     $node->manakai_local_name eq 'div' and
140     not $has_div) {
141     ## TODO: SHOULD be suitable for handling as HTML [XHTML10]
142     $has_div = 1;
143     } else {
144     $self->{onerror}->(node => $node, type => 'element not allowed');
145     }
146     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
147     unshift @nodes, @$sib;
148     push @$new_todos, @$ch;
149     } elsif ($nt == 3 or $nt == 4) {
150     ## TODO: Are white spaces allowed?
151     $self->{onerror}->(node => $node, type => 'character not allowed');
152     } elsif ($nt == 5) {
153     unshift @nodes, @{$node->child_nodes};
154     }
155     }
156    
157     unless ($has_div) {
158     $self->{onerror}->(node => $todo->{node},
159     type => 'element missing:div');
160     }
161    
162     return ($new_todos);
163     }
164    
165     },
166     }; # $AtomTextConstruct
167    
168     my $AtomPersonConstruct = {
169     attrs_checker => $GetAtomAttrsChecker->({}),
170     checker => sub {
171     my ($self, $todo) = @_;
172    
173     my @nodes = (@{$todo->{node}->child_nodes});
174     my $new_todos = [];
175    
176     my $has_name;
177     my $has_uri;
178     my $has_email;
179     while (@nodes) {
180     my $node = shift @nodes;
181     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
182    
183     my $nt = $node->node_type;
184     if ($nt == 1) {
185     # MUST
186     my $nsuri = $node->namespace_uri;
187     $nsuri = '' unless defined $nsuri;
188     my $not_allowed;
189     if ($nsuri eq $ATOM_NS) {
190     my $ln = $node->manakai_local_name;
191     if ($ln eq 'name') {
192     unless ($has_name) {
193     $has_name = 1;
194     } else {
195     $not_allowed = 1;
196     }
197     } elsif ($ln eq 'uri') {
198     unless ($has_uri) {
199     $has_uri = 1;
200     } else {
201     $not_allowed = 1; # MUST NOT
202     }
203     } elsif ($ln eq 'email') {
204     unless ($has_email) {
205     $has_email = 1;
206     } else {
207     $not_allowed = 1; # MUST NOT
208     }
209     } else {
210     $not_allowed = 1;
211     }
212     } else {
213     ## TODO: extension element
214     $not_allowed = 1;
215     }
216     $self->{onerror}->(node => $node, type => 'element not allowed')
217     if $not_allowed;
218     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
219     unshift @nodes, @$sib;
220     push @$new_todos, @$ch;
221     } elsif ($nt == 3 or $nt == 4) {
222     ## TODO: Are white spaces allowed?
223     $self->{onerror}->(node => $node, type => 'character not allowed');
224     } elsif ($nt == 5) {
225     unshift @nodes, @{$node->child_nodes};
226     }
227     }
228    
229     unless ($has_name) { # MUST
230     $self->{onerror}->(node => $todo->{node},
231     type => 'element missing:atom.name');
232     }
233    
234     return ($new_todos);
235     },
236     }; # $AtomPersonConstruct
237    
238 wakaba 1.2 our $Element;
239    
240     $Element->{$ATOM_NS}->{name} = {
241     ## NOTE: Strictly speaking, structure and semantics for atom:name
242     ## element outside of Person construct is not defined.
243     attrs_checker => $GetAtomAttrsChecker->({}),
244     checker => sub {
245     my ($self, $todo) = @_;
246    
247     my @nodes = (@{$todo->{node}->child_nodes});
248     my $new_todos = [];
249    
250     while (@nodes) {
251     my $node = shift @nodes;
252     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
253    
254     my $nt = $node->node_type;
255     if ($nt == 1) {
256     ## NOTE: No constraint.
257     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
258     unshift @nodes, @$sib;
259     push @$new_todos, @$ch;
260     } elsif ($nt == 3 or $nt == 4) {
261     #
262     } elsif ($nt == 5) {
263     unshift @nodes, @{$node->child_nodes};
264     }
265     }
266    
267     return ($new_todos);
268     },
269     };
270    
271     $Element->{$ATOM_NS}->{uri} = {
272     ## NOTE: Strictly speaking, structure and semantics for atom:uri
273     ## element outside of Person construct is not defined.
274     attrs_checker => $GetAtomAttrsChecker->({}),
275     checker => sub {
276     my ($self, $todo) = @_;
277    
278     my @nodes = (@{$todo->{node}->child_nodes});
279     my $new_todos = [];
280    
281     my $s = '';
282     while (@nodes) {
283     my $node = shift @nodes;
284     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
285    
286     my $nt = $node->node_type;
287     if ($nt == 1) {
288     ## NOTE: Not explicitly disallowed.
289     $self->{onerror}->(node => $node, type => 'element not allowed');
290     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
291     unshift @nodes, @$sib;
292     push @$new_todos, @$ch;
293     } elsif ($nt == 3 or $nt == 4) {
294     $s .= $node->data;
295     } elsif ($nt == 5) {
296     unshift @nodes, @{$node->child_nodes};
297     }
298     }
299    
300     ## NOTE: There MUST NOT be any white space.
301     Whatpm::URIChecker->check_iri_reference ($s, sub {
302     my %opt = @_;
303     $self->{onerror}->(node => $todo->{node}, level => $opt{level},
304     type => 'URI::'.$opt{type}.
305     (defined $opt{position} ? ':'.$opt{position} : ''));
306     });
307    
308     return ($new_todos);
309     },
310     };
311    
312     $Element->{$ATOM_NS}->{email} = {
313     ## NOTE: Strictly speaking, structure and semantics for atom:email
314     ## element outside of Person construct is not defined.
315     attrs_checker => $GetAtomAttrsChecker->({}),
316     checker => sub {
317     my ($self, $todo) = @_;
318    
319     my @nodes = (@{$todo->{node}->child_nodes});
320     my $new_todos = [];
321    
322     my $s = '';
323     while (@nodes) {
324     my $node = shift @nodes;
325     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
326    
327     my $nt = $node->node_type;
328     if ($nt == 1) {
329     ## NOTE: Not explicitly disallowed.
330     $self->{onerror}->(node => $node, type => 'element not allowed');
331     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
332     unshift @nodes, @$sib;
333     push @$new_todos, @$ch;
334     } elsif ($nt == 3 or $nt == 4) {
335     $s .= $node->data;
336     } elsif ($nt == 5) {
337     unshift @nodes, @{$node->child_nodes};
338     }
339     }
340    
341     ## TODO: addr-spec
342     $self->{onerror}->(node => $todo->{node}, type => 'addr-spec',
343     level => 'unsupported');
344    
345     return ($new_todos);
346     },
347     };
348    
349 wakaba 1.1 ## MUST NOT be any white space
350     my $AtomDateConstruct = {
351     attrs_checker => $GetAtomAttrsChecker->({}),
352     checker => sub {
353     my ($self, $todo) = @_;
354    
355     my @nodes = (@{$todo->{node}->child_nodes});
356     my $new_todos = [];
357    
358     my $s = '';
359     while (@nodes) {
360     my $node = shift @nodes;
361     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
362    
363     my $nt = $node->node_type;
364     if ($nt == 1) {
365     ## NOTE: It does not explicitly say that there MUST NOT be any element.
366     $self->{onerror}->(node => $node, type => 'element not allowed');
367     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
368     unshift @nodes, @$sib;
369     push @$new_todos, @$ch;
370     } elsif ($nt == 3 or $nt == 4) {
371     $s .= $node->data;
372     } elsif ($nt == 5) {
373     unshift @nodes, @{$node->child_nodes};
374     }
375     }
376    
377 wakaba 1.4 ## MUST: RFC 3339 |date-time| with uppercase |T| and |Z|
378     if ($s =~ /\A([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?>\.[0-9]+)?(?>Z|[+-]([0-9]{2}):([0-9]{2}))\z/) {
379     my ($y, $M, $d, $h, $m, $s, $zh, $zm)
380     = ($1, $2, $3, $4, $5, $6, $7, $8);
381     my $node = $todo->{node};
382    
383     ## Check additional constraints described or referenced in
384     ## comments of ABNF rules for |date-time|.
385     my $level = $self->{must_level};
386     if (0 < $M and $M < 13) {
387     $self->{onerror}->(node => $node, type => 'datetime:bad day',
388     level => $level)
389     if $d < 1 or
390     $d > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$M];
391     $self->{onerror}->(node => $node, type => 'datetime:bad day',
392     level => $level)
393     if $M == 2 and $d == 29 and
394     not ($y % 400 == 0 or ($y % 4 == 0 and $y % 100 != 0));
395     } else {
396     $self->{onerror}->(node => $node, type => 'datetime:bad month',
397     level => $level);
398     }
399     $self->{onerror}->(node => $node, type => 'datetime:bad hour',
400     level => $level) if $h > 23;
401     $self->{onerror}->(node => $node, type => 'datetime:bad minute',
402     level => $level) if $m > 59;
403     $self->{onerror}->(node => $node, type => 'datetime:bad second',
404     level => $level)
405     if $s > 60; ## NOTE: Validness of leap seconds are not checked.
406     $self->{onerror}->(node => $node, type => 'datetime:bad timezone hour',
407     level => $level) if $zh > 23;
408     $self->{onerror}->(node => $node, type => 'datetime:bad timezone minute',
409     level => $level) if $zm > 59;
410     } else {
411     $self->{onerror}->(node => $todo->{node},
412     type => 'datetime:syntax error',
413     level => $self->{must_level});
414     }
415     ## NOTE: SHOULD be accurate as possible (cannot be checked)
416 wakaba 1.1
417     return ($new_todos);
418     },
419     }; # $AtomDateConstruct
420    
421 wakaba 1.2 $Element->{$ATOM_NS}->{entry} = {
422 wakaba 1.1 is_root => 1,
423     attrs_checker => $GetAtomAttrsChecker->({}),
424     checker => sub {
425     my ($self, $todo) = @_;
426    
427     my @nodes = (@{$todo->{node}->child_nodes});
428     my $new_todos = [];
429    
430     ## TODO: MUST author+ unless (child::source/child::author)
431     ## or (parent::feed/child::author)
432    
433     my $has_element = {};
434     while (@nodes) {
435     my $node = shift @nodes;
436     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
437    
438     my $nt = $node->node_type;
439     if ($nt == 1) {
440     # MUST
441     my $nsuri = $node->namespace_uri;
442     $nsuri = '' unless defined $nsuri;
443     my $not_allowed;
444     if ($nsuri eq $ATOM_NS) {
445     my $ln = $node->manakai_local_name;
446     if ({ # MUST (0, 1)
447     content => 1,
448     id => 1,
449     published => 1,
450     rights => 1,
451     source => 1,
452     summary => 1,
453     ## TODO: MUST if child::content/@src | child::content/@type = IMT, !text/ !/xml !+xml
454     title => 1,
455     updated => 1,
456     }->{$ln}) {
457     unless ($has_element->{$ln}) {
458     $has_element->{$ln} = 1;
459     $not_allowed = $has_element->{entry};
460     } else {
461     $not_allowed = 1;
462     }
463     } elsif ($ln eq 'link') { # MAY
464 wakaba 1.3 if ($node->rel eq $LINK_REL . 'alternate') {
465     my $type = $node->get_attribute_ns (undef, 'type');
466     $type = '' unless defined $type;
467     my $hreflang = $node->get_attribute_ns (undef, 'hreflang');
468     $hreflang = '' unless defined $hreflang;
469     my $key = 'link:'.(defined $type ? ':'.$type : '').':'.
470     (defined $hreflang ? ':'.$hreflang : '');
471     unless ($has_element->{$key}) {
472     $has_element->{$key} = 1;
473     $has_element->{'link.alternate'} = 1;
474     } else {
475     $not_allowed = 1;
476     }
477     }
478    
479 wakaba 1.1 ## NOTE: MAY
480 wakaba 1.3 $not_allowed ||= $has_element->{entry};
481 wakaba 1.1 } elsif ({ # MAY
482     author => 1,
483     category => 1,
484     contributor => 1,
485     }->{$ln}) {
486     $not_allowed = $has_element->{entry};
487     } else {
488     $not_allowed = 1;
489     }
490     } else {
491     ## TODO: extension element
492     $not_allowed = 1;
493     }
494     $self->{onerror}->(node => $node, type => 'element not allowed')
495     if $not_allowed;
496     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
497     unshift @nodes, @$sib;
498     push @$new_todos, @$ch;
499     } elsif ($nt == 3 or $nt == 4) {
500     ## TODO: Are white spaces allowed?
501     $self->{onerror}->(node => $node, type => 'character not allowed');
502     } elsif ($nt == 5) {
503     unshift @nodes, @{$node->child_nodes};
504     }
505     }
506    
507     ## NOTE: metadata elements, followed by atom:entry* (no explicit MAY)
508    
509     ## TODO: If entry's with same id, then updated SHOULD be different
510    
511     unless ($has_element->{id}) { # MUST
512     $self->{onerror}->(node => $todo->{node},
513     type => 'element missing:atom.id');
514     }
515     unless ($has_element->{title}) { # MUST
516     $self->{onerror}->(node => $todo->{node},
517     type => 'element missing:atom.title');
518     }
519     unless ($has_element->{updated}) { # MUST
520     $self->{onerror}->(node => $todo->{node},
521     type => 'element missing:atom.updated');
522     }
523 wakaba 1.3 if (not $has_element->{content} and
524     not $has_element->{'link.alternate'}) {
525     $self->{onerror}->(node => $todo->{node},
526     type => 'element missing:atom.link.alternate');
527     }
528 wakaba 1.1
529     return ($new_todos);
530     },
531     };
532    
533     $Element->{$ATOM_NS}->{feed} = {
534     is_root => 1,
535     attrs_checker => $GetAtomAttrsChecker->({}),
536     checker => sub {
537     my ($self, $todo) = @_;
538    
539     my @nodes = (@{$todo->{node}->child_nodes});
540     my $new_todos = [];
541    
542     ## TODO: MUST author+ unless all entry child has author+.
543    
544     my $has_element = {};
545     while (@nodes) {
546     my $node = shift @nodes;
547     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
548    
549     my $nt = $node->node_type;
550     if ($nt == 1) {
551     my $nsuri = $node->namespace_uri;
552     $nsuri = '' unless defined $nsuri;
553     my $not_allowed;
554     if ($nsuri eq $ATOM_NS) {
555     my $ln = $node->manakai_local_name;
556     if ($ln eq 'entry') {
557     $has_element->{entry} = 1;
558     } elsif ({ # MUST (0, 1)
559     generator => 1,
560     icon => 1,
561     id => 1,
562     logo => 1,
563     rights => 1,
564     subtitle => 1,
565     title => 1,
566     updated => 1,
567     }->{$ln}) {
568     unless ($has_element->{$ln}) {
569     $has_element->{$ln} = 1;
570     $not_allowed = $has_element->{entry};
571     } else {
572     $not_allowed = 1;
573     }
574 wakaba 1.3 } elsif ($ln eq 'link') {
575     my $rel = $node->rel;
576     if ($rel eq $LINK_REL . 'alternate') {
577     my $type = $node->get_attribute_ns (undef, 'type');
578     $type = '' unless defined $type;
579     my $hreflang = $node->get_attribute_ns (undef, 'hreflang');
580     $hreflang = '' unless defined $hreflang;
581     my $key = 'link:'.(defined $type ? ':'.$type : '').':'.
582     (defined $hreflang ? ':'.$hreflang : '');
583     unless ($has_element->{$key}) {
584     $has_element->{$key} = 1;
585     } else {
586     $not_allowed = 1;
587     }
588     } elsif ($rel eq $LINK_REL . 'self') {
589     $has_element->{'link.self'} = 1;
590     }
591    
592     ## NOTE: MAY
593 wakaba 1.1 $not_allowed = $has_element->{entry};
594     } elsif ({ # MAY
595     author => 1,
596     category => 1,
597     contributor => 1,
598     }->{$ln}) {
599     $not_allowed = $has_element->{entry};
600     } else {
601     $not_allowed = 1;
602     }
603     } else {
604     ## TODO: extension element
605     $not_allowed = 1;
606     }
607     $self->{onerror}->(node => $node, type => 'element not allowed')
608     if $not_allowed;
609     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
610     unshift @nodes, @$sib;
611     push @$new_todos, @$ch;
612     } elsif ($nt == 3 or $nt == 4) {
613     ## TODO: Are white spaces allowed?
614     $self->{onerror}->(node => $node, type => 'character not allowed');
615     } elsif ($nt == 5) {
616     unshift @nodes, @{$node->child_nodes};
617     }
618     }
619    
620     ## NOTE: metadata elements, followed by atom:entry* (no explicit MAY)
621    
622     ## TODO: If entry's with same id, then updated SHOULD be different
623    
624     unless ($has_element->{id}) { # MUST
625     $self->{onerror}->(node => $todo->{node},
626     type => 'element missing:atom.id');
627     }
628     unless ($has_element->{title}) { # MUST
629     $self->{onerror}->(node => $todo->{node},
630     type => 'element missing:atom.title');
631     }
632     unless ($has_element->{updated}) { # MUST
633     $self->{onerror}->(node => $todo->{node},
634     type => 'element missing:atom.updated');
635     }
636 wakaba 1.3 unless ($has_element->{'link.self'}) {
637     $self->{onerror}->(node => $todo->{node}, level => 's',
638     type => 'child element missing:atom.link.self');
639     }
640 wakaba 1.1
641     return ($new_todos);
642     },
643     };
644    
645     $Element->{$ATOM_NS}->{content} = {
646     attrs_checker => $GetAtomAttrsChecker->({
647     src => sub { 1 }, # checked in |checker|
648     type => sub { 1 }, # checked in |checker|
649     }),
650     checker => sub {
651     my ($self, $todo) = @_;
652    
653     my $attr = $todo->{node}->get_attribute_node_ns (undef, 'type');
654     my $src_attr = $todo->{node}->get_attribute_node_ns (undef, 'src');
655     my $value;
656     if ($attr) {
657     $value = $attr->value;
658     if ($value eq 'text' or $value eq 'html' or $value eq 'xhtml') {
659     # MUST
660     } else {
661 wakaba 1.3 ## NOTE: MUST be a MIME media type. What is "MIME media type"?
662     my $value = $attr->value;
663     my $lws0 = qr/(?>(?>\x0D\x0A)?[\x09\x20])*/;
664     my $token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+/;
665     my $qs = qr/"(?>[\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\x7E]|\x0D\x0A[\x09\x20]|\x5C[\x00-\x7F])*"/;
666     if ($value =~ m#\A$lws0($token)$lws0/$lws0($token)$lws0((?>;$lws0$token$lws0=$lws0(?>$token|$qs)$lws0)*)\z#) {
667     my @type = ($1, $2);
668     my $param = $3;
669     while ($param =~ s/^;$lws0($token)$lws0=$lws0(?>($token)|($qs))$lws0//) {
670     if (defined $2) {
671     push @type, $1 => $2;
672     } else {
673     my $n = $1;
674     my $v = $2;
675     $v =~ s/\\(.)/$1/gs;
676     push @type, $n => $v;
677     }
678     }
679     require Whatpm::IMTChecker;
680     Whatpm::IMTChecker->check_imt (sub {
681     my %opt = @_;
682     $self->{onerror}->(node => $attr, level => $opt{level},
683     type => 'IMT:'.$opt{type});
684     }, @type);
685     } else {
686     $self->{onerror}->(node => $attr, type => 'IMT:syntax error');
687     }
688 wakaba 1.1 }
689     } elsif ($src_attr) {
690     $value = '';
691     $self->{onerror}->(node => $todo->{node},
692     type => 'attribute missing:type', level => 's');
693     } else {
694     $value = 'text';
695     }
696    
697     ## TODO: This implementation is not optimal.
698    
699     if ($src_attr) {
700 wakaba 1.2 ## NOTE: There MUST NOT be any white space.
701     Whatpm::URIChecker->check_iri_reference ($src_attr->value, sub {
702     my %opt = @_;
703     $self->{onerror}->(node => $todo->{node}, level => $opt{level},
704     type => 'URI::'.$opt{type}.
705     (defined $opt{position} ? ':'.$opt{position} : ''));
706     });
707 wakaba 1.1
708     ## NOTE: If @src, the element MUST be empty. What is "empty"?
709     ## Is |<e><!----></e>| empty? |<e>&e;</e>| where |&e;| has
710     ## empty replacement tree shuld be empty, since Atom is defined
711     ## in terms of XML Information Set where entities are expanded.
712     ## (but what if |&e;| is an unexpanded entity?)
713     }
714    
715     if ($value eq 'text') {
716     $self->{onerror}->(node => $attr, type => 'not IMT') if $src_attr;
717    
718     my @nodes = (@{$todo->{node}->child_nodes});
719     my $new_todos = [];
720    
721     while (@nodes) {
722     my $node = shift @nodes;
723     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
724    
725     my $nt = $node->node_type;
726     if ($nt == 1) {
727     # MUST NOT
728     $self->{onerror}->(node => $node, type => 'element not allowed');
729     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
730     unshift @nodes, @$sib;
731     push @$new_todos, @$ch;
732     } elsif ($nt == 3 or $nt == 4) {
733     $self->{onerror}->(node => $node, type => 'character not allowed')
734     if $src_attr;
735     } elsif ($nt == 5) {
736     unshift @nodes, @{$node->child_nodes};
737     }
738     }
739    
740     return ($new_todos);
741     } elsif ($value eq 'html') {
742     $self->{onerror}->(node => $attr, type => 'not IMT') if $src_attr;
743    
744     my @nodes = (@{$todo->{node}->child_nodes});
745     my $new_todos = [];
746    
747     while (@nodes) {
748     my $node = shift @nodes;
749     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
750    
751     my $nt = $node->node_type;
752     if ($nt == 1) {
753     # MUST NOT
754     $self->{onerror}->(node => $node, type => 'element not allowed');
755     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
756     unshift @nodes, @$sib;
757     push @$new_todos, @$ch;
758     } elsif ($nt == 3 or $nt == 4) {
759     $self->{onerror}->(node => $node, type => 'character not allowed')
760     if $src_attr;
761     } elsif ($nt == 5) {
762     unshift @nodes, @{$node->child_nodes};
763     }
764     }
765    
766     ## TODO: SHOULD be suitable for handling as HTML [HTML4]
767     # markup MUST be escaped
768     ## TODO: HTML SHOULD be valid as if within <div>
769    
770     return ($new_todos);
771     } elsif ($value eq 'xhtml') {
772     $self->{onerror}->(node => $attr, type => 'not IMT') if $src_attr;
773    
774     my @nodes = (@{$todo->{node}->child_nodes});
775     my $new_todos = [];
776    
777     my $has_div;
778     while (@nodes) {
779     my $node = shift @nodes;
780     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
781    
782     my $nt = $node->node_type;
783     if ($nt == 1) {
784     # MUST
785     my $nsuri = $node->namespace_uri;
786     if (defined $nsuri and
787     $nsuri eq q<http://www.w3.org/1999/xhtml> and
788     $node->manakai_local_name eq 'div' and
789     not $has_div) {
790     ## TODO: SHOULD be suitable for handling as HTML [XHTML10]
791     $has_div = 1;
792     $self->{onerror}->(node => $node, type => 'element not allowed')
793     if $src_attr;
794     } else {
795     $self->{onerror}->(node => $node, type => 'element not allowed');
796     }
797     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
798     unshift @nodes, @$sib;
799     push @$new_todos, @$ch;
800     } elsif ($nt == 3 or $nt == 4) {
801     ## TODO: Are white spaces allowed?
802     $self->{onerror}->(node => $node, type => 'character not allowed');
803     } elsif ($nt == 5) {
804     unshift @nodes, @{$node->child_nodes};
805     }
806     }
807    
808     unless ($has_div) {
809     $self->{onerror}->(node => $todo->{node},
810     type => 'element missing:div');
811     }
812    
813     return ($new_todos);
814     } elsif ($value =~ m![+/][Xx][Mm][Ll]\z!) {
815     ## ISSUE: There is no definition for "XML media type" in RFC 3023.
816     ## Is |application/xml-dtd| an XML media type?
817    
818     my @nodes = (@{$todo->{node}->child_nodes});
819     my $new_todos = [];
820    
821     while (@nodes) {
822     my $node = shift @nodes;
823     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
824    
825     my $nt = $node->node_type;
826     if ($nt == 1) {
827     ## MAY contain elements
828     $self->{onerror}->(node => $node, type => 'element not allowed')
829     if $src_attr;
830     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
831     unshift @nodes, @$sib;
832     push @$new_todos, @$ch;
833     } elsif ($nt == 3 or $nt == 4) {
834     ## TODO: Are white spaces allowed?
835     $self->{onerror}->(node => $node, type => 'character not allowed');
836     } elsif ($nt == 5) {
837     unshift @nodes, @{$node->child_nodes};
838     }
839     }
840    
841 wakaba 1.3 ## NOTE: SHOULD be suitable for handling as $value.
842 wakaba 1.1 ## If no @src, this would normally mean it contains a
843     ## single child element that would serve as the root element.
844 wakaba 1.3 $self->{onerror}->(node => $todo->{node}, level => 'unsupported',
845     type => 'content:'.$value);
846 wakaba 1.1
847     return ($new_todos);
848     } elsif ($value =~ m!^[Tt][Ee][Xx][Tt]/!) {
849     my @nodes = (@{$todo->{node}->child_nodes});
850     my $new_todos = [];
851    
852     while (@nodes) {
853     my $node = shift @nodes;
854     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
855    
856     my $nt = $node->node_type;
857     if ($nt == 1) {
858     # MUST NOT
859     $self->{onerror}->(node => $node, type => 'element not allowed');
860     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
861     unshift @nodes, @$sib;
862     push @$new_todos, @$ch;
863     } elsif ($nt == 3 or $nt == 4) {
864     $self->{onerror}->(node => $node, type => 'character not allowed')
865     if $src_attr;
866     } elsif ($nt == 5) {
867     unshift @nodes, @{$node->child_nodes};
868     }
869     }
870    
871     ## NOTE: No further restriction (such as to conform to the type).
872    
873     return ($new_todos);
874     } else {
875     my @nodes = (@{$todo->{node}->child_nodes});
876     my $new_todos = [];
877    
878     if ($value =~ m!^(?>message|multipart)/!i) { # MUST NOT
879     $self->{onerror}->(node => $attr, type => 'IMT:composite');
880     }
881    
882     my $s = '';
883     while (@nodes) {
884     my $node = shift @nodes;
885     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
886    
887     my $nt = $node->node_type;
888     if ($nt == 1) {
889     ## not explicitly disallowed
890     $self->{onerror}->(node => $node, type => 'element not allowed');
891     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
892     unshift @nodes, @$sib;
893     push @$new_todos, @$ch;
894     } elsif ($nt == 3 or $nt == 4) {
895     $s .= $node->data;
896     $self->{onerror}->(node => $node, type => 'character not allowed')
897     if $src_attr;
898     } elsif ($nt == 5) {
899     unshift @nodes, @{$node->child_nodes};
900     }
901     }
902    
903     ## TODO: $s = valid Base64ed [RFC 3548] where
904     ## MAY leading and following "white space" (what?)
905     ## and lines separated by a single U+000A
906 wakaba 1.3
907     ## NOTE: SHOULD be suitable for the indicated media type.
908     $self->{onerror}->(node => $todo->{node}, level => 'unsupported',
909     type => 'content:'.$value);
910 wakaba 1.1
911     return ($new_todos);
912     }
913     },
914     };
915    
916     $Element->{$ATOM_NS}->{author} = $AtomPersonConstruct;
917    
918     $Element->{$ATOM_NS}->{category} = {
919     attrs_checker => $GetAtomAttrsChecker->({
920     label => sub { 1 }, # no value constraint
921 wakaba 1.2 scheme => sub { # NOTE: No MUST.
922     my ($self, $attr) = @_;
923     ## NOTE: There MUST NOT be any white space.
924     Whatpm::URIChecker->check_iri ($attr->value, sub {
925     my %opt = @_;
926     $self->{onerror}->(node => $attr, level => $opt{level},
927     type => 'URI::'.$opt{type}.
928     (defined $opt{position} ? ':'.$opt{position} : ''));
929     });
930     },
931 wakaba 1.1 term => sub { 1 }, # no value constraint
932     }),
933     checker => sub {
934     my ($self, $todo) = @_;
935    
936     unless ($todo->{node}->has_attribute_ns (undef, 'term')) {
937     $self->{onerror}->(node => $todo->{node},
938     type => 'attribute missing:term');
939     }
940    
941     my @nodes = (@{$todo->{node}->child_nodes});
942     my $new_todos = [];
943    
944     while (@nodes) {
945     my $node = shift @nodes;
946     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
947    
948     my $nt = $node->node_type;
949     if ($nt == 1) {
950     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
951     unshift @nodes, @$sib;
952     push @$new_todos, @$ch;
953     } elsif ($nt == 3 or $nt == 4) {
954     #
955     } elsif ($nt == 5) {
956     unshift @nodes, @{$node->child_nodes};
957     }
958     }
959    
960     return ($new_todos);
961     },
962     };
963    
964     $Element->{$ATOM_NS}->{contributor} = $AtomPersonConstruct;
965    
966     $Element->{$ATOM_NS}->{generator} = {
967     attrs_checker => $GetAtomAttrsChecker->({
968 wakaba 1.2 uri => sub { # MUST
969     my ($self, $attr) = @_;
970     ## NOTE: There MUST NOT be any white space.
971     Whatpm::URIChecker->check_iri_reference ($attr->value, sub {
972     my %opt = @_;
973     $self->{onerror}->(node => $attr, level => $opt{level},
974     type => 'URI::'.$opt{type}.
975     (defined $opt{position} ? ':'.$opt{position} : ''));
976     });
977     ## NOTE: Dereferencing SHOULD produce a representation
978     ## that is relevant to the agent.
979     },
980 wakaba 1.1 version => sub { 1 }, # no value constraint
981     }),
982     checker => sub {
983     my ($self, $todo) = @_;
984    
985     my @nodes = (@{$todo->{node}->child_nodes});
986     my $new_todos = [];
987    
988     while (@nodes) {
989     my $node = shift @nodes;
990     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
991    
992     my $nt = $node->node_type;
993     if ($nt == 1) {
994     ## not explicitly disallowed
995     $self->{onerror}->(node => $node, type => 'element not allowed');
996     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
997     unshift @nodes, @$sib;
998     push @$new_todos, @$ch;
999     } elsif ($nt == 3 or $nt == 4) {
1000     ## MUST be a string that is a human-readable name for
1001     ## the generating agent
1002     } elsif ($nt == 5) {
1003     unshift @nodes, @{$node->child_nodes};
1004     }
1005     }
1006    
1007     return ($new_todos);
1008     },
1009     };
1010    
1011     $Element->{$ATOM_NS}->{icon} = {
1012     attrs_checker => $GetAtomAttrsChecker->({}),
1013     checker => sub {
1014     my ($self, $todo) = @_;
1015    
1016     my @nodes = (@{$todo->{node}->child_nodes});
1017     my $new_todos = [];
1018    
1019 wakaba 1.2 my $s = '';
1020 wakaba 1.1 while (@nodes) {
1021     my $node = shift @nodes;
1022     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1023    
1024     my $nt = $node->node_type;
1025     if ($nt == 1) {
1026     ## not explicitly disallowed
1027     $self->{onerror}->(node => $node, type => 'element not allowed');
1028     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
1029     unshift @nodes, @$sib;
1030     push @$new_todos, @$ch;
1031     } elsif ($nt == 3 or $nt == 4) {
1032 wakaba 1.2 $s .= $node->data;
1033 wakaba 1.1 } elsif ($nt == 5) {
1034     unshift @nodes, @{$node->child_nodes};
1035     }
1036     }
1037    
1038 wakaba 1.2 ## NOTE: No MUST.
1039     ## NOTE: There MUST NOT be any white space.
1040     Whatpm::URIChecker->check_iri_reference ($s, sub {
1041     my %opt = @_;
1042     $self->{onerror}->(node => $todo->{node}, level => $opt{level},
1043     type => 'URI::'.$opt{type}.
1044     (defined $opt{position} ? ':'.$opt{position} : ''));
1045     });
1046    
1047 wakaba 1.1 ## NOTE: Image SHOULD be 1:1 and SHOULD be small
1048    
1049     return ($new_todos);
1050     },
1051     };
1052    
1053     $Element->{$ATOM_NS}->{id} = {
1054     attrs_checker => $GetAtomAttrsChecker->({}),
1055     checker => sub {
1056     my ($self, $todo) = @_;
1057    
1058     my @nodes = (@{$todo->{node}->child_nodes});
1059     my $new_todos = [];
1060 wakaba 1.2
1061     my $s = '';
1062     while (@nodes) {
1063     my $node = shift @nodes;
1064     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1065    
1066     my $nt = $node->node_type;
1067     if ($nt == 1) {
1068     ## not explicitly disallowed
1069     $self->{onerror}->(node => $node, type => 'element not allowed');
1070     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
1071     unshift @nodes, @$sib;
1072     push @$new_todos, @$ch;
1073     } elsif ($nt == 3 or $nt == 4) {
1074     $s .= $node->data;
1075     } elsif ($nt == 5) {
1076     unshift @nodes, @{$node->child_nodes};
1077     }
1078     }
1079    
1080     ## NOTE: There MUST NOT be any white space.
1081     Whatpm::URIChecker->check_iri ($s, sub { # MUST
1082     my %opt = @_;
1083     $self->{onerror}->(node => $todo->{node}, level => $opt{level},
1084     type => 'URI::'.$opt{type}.
1085     (defined $opt{position} ? ':'.$opt{position} : ''));
1086     });
1087     ## TODO: SHOULD be normalized
1088    
1089     return ($new_todos);
1090     },
1091     };
1092    
1093     $Element->{$ATOM_NS}->{link} = {
1094     attrs_checker => $GetAtomAttrsChecker->({
1095     href => sub {
1096     my ($self, $attr) = @_;
1097     ## NOTE: There MUST NOT be any white space.
1098     Whatpm::URIChecker->check_iri_reference ($attr->value, sub {
1099     my %opt = @_;
1100     $self->{onerror}->(node => $attr, level => $opt{level},
1101     type => 'URI::'.$opt{type}.
1102     (defined $opt{position} ? ':'.$opt{position} : ''));
1103     });
1104     },
1105 wakaba 1.4 hreflang => $AtomLanguageTagAttrChecker,
1106 wakaba 1.2 length => sub { }, # No MUST; in octets.
1107     rel => sub { # MUST
1108     my ($self, $attr) = @_;
1109     my $value = $attr->value;
1110     if ($value =~ /\A(?>[0-9A-Za-z._~!\$&'()*+,;=\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}-]|%[0-9A-Fa-f][0-9A-Fa-f]|\@)+\z/) {
1111 wakaba 1.3 $value = $LINK_REL . $value;
1112 wakaba 1.2 }
1113    
1114     ## NOTE: There MUST NOT be any white space.
1115     Whatpm::URIChecker->check_iri ($value, sub {
1116     my %opt = @_;
1117     $self->{onerror}->(node => $attr, level => $opt{level},
1118     type => 'URI::'.$opt{type}.
1119     (defined $opt{position} ? ':'.$opt{position} : ''));
1120     });
1121    
1122     ## TODO: Warn if unregistered
1123     },
1124 wakaba 1.5 title => sub { }, # No MUST
1125 wakaba 1.3 type => sub {
1126     ## NOTE: MUST be a MIME media type. What is "MIME media type"?
1127     my ($self, $attr) = @_;
1128     my $value = $attr->value;
1129     my $lws0 = qr/(?>(?>\x0D\x0A)?[\x09\x20])*/;
1130     my $token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+/;
1131     my $qs = qr/"(?>[\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\x7E]|\x0D\x0A[\x09\x20]|\x5C[\x00-\x7F])*"/;
1132     if ($value =~ m#\A$lws0($token)$lws0/$lws0($token)$lws0((?>;$lws0$token$lws0=$lws0(?>$token|$qs)$lws0)*)\z#) {
1133     my @type = ($1, $2);
1134     my $param = $3;
1135     while ($param =~ s/^;$lws0($token)$lws0=$lws0(?>($token)|($qs))$lws0//) {
1136     if (defined $2) {
1137     push @type, $1 => $2;
1138     } else {
1139     my $n = $1;
1140     my $v = $2;
1141     $v =~ s/\\(.)/$1/gs;
1142     push @type, $n => $v;
1143     }
1144     }
1145     require Whatpm::IMTChecker;
1146     Whatpm::IMTChecker->check_imt (sub {
1147     my %opt = @_;
1148     $self->{onerror}->(node => $attr, level => $opt{level},
1149     type => 'IMT:'.$opt{type});
1150     }, @type);
1151     } else {
1152     $self->{onerror}->(node => $attr, type => 'IMT:syntax error');
1153     }
1154     },
1155 wakaba 1.2 }),
1156     checker => sub {
1157     my ($self, $todo) = @_;
1158    
1159     unless ($todo->{node}->has_attribute_ns (undef, 'href')) { # MUST
1160     $self->{onerror}->(node => $todo->{node},
1161     type => 'attribute missing:href');
1162     }
1163    
1164 wakaba 1.3 if ($todo->{node}->rel eq $LINK_REL . 'enclosure' and
1165 wakaba 1.2 not $todo->{node}->has_attribute_ns (undef, 'length')) {
1166     $self->{onerror}->(node => $todo->{node}, level => 's',
1167     type => 'attribute missing:length');
1168     }
1169    
1170     my @nodes = (@{$todo->{node}->child_nodes});
1171     my $new_todos = [];
1172 wakaba 1.1
1173     while (@nodes) {
1174     my $node = shift @nodes;
1175     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1176    
1177     my $nt = $node->node_type;
1178     if ($nt == 1) {
1179 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node, $todo);
1180     unshift @nodes, @$sib;
1181     push @$new_todos, @$ch;
1182     } elsif ($nt == 3 or $nt == 4) {
1183     #
1184     } elsif ($nt == 5) {
1185     unshift @nodes, @{$node->child_nodes};
1186     }
1187     }
1188    
1189     return ($new_todos);
1190     },
1191     };
1192    
1193     $Element->{$ATOM_NS}->{logo} = {
1194     attrs_checker => $GetAtomAttrsChecker->({}),
1195     checker => sub {
1196     my ($self, $todo) = @_;
1197    
1198     my @nodes = (@{$todo->{node}->child_nodes});
1199     my $new_todos = [];
1200    
1201     my $s = '';
1202     while (@nodes) {
1203     my $node = shift @nodes;
1204     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1205    
1206     my $nt = $node->node_type;
1207     if ($nt == 1) {
1208 wakaba 1.1 ## not explicitly disallowed
1209     $self->{onerror}->(node => $node, type => 'element not allowed');
1210     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
1211     unshift @nodes, @$sib;
1212     push @$new_todos, @$ch;
1213     } elsif ($nt == 3 or $nt == 4) {
1214 wakaba 1.2 $s .= $node->data;
1215 wakaba 1.1 } elsif ($nt == 5) {
1216     unshift @nodes, @{$node->child_nodes};
1217     }
1218     }
1219    
1220 wakaba 1.2 ## NOTE: There MUST NOT be any white space.
1221     Whatpm::URIChecker->check_iri_reference ($s, sub {
1222     my %opt = @_;
1223     $self->{onerror}->(node => $todo->{node}, level => $opt{level},
1224     type => 'URI::'.$opt{type}.
1225     (defined $opt{position} ? ':'.$opt{position} : ''));
1226     });
1227    
1228     ## NOTE: Image SHOULD be 2:1
1229    
1230     return ($new_todos);
1231     },
1232     };
1233    
1234     $Element->{$ATOM_NS}->{published} = $AtomDateConstruct;
1235    
1236     $Element->{$ATOM_NS}->{rights} = $AtomDateConstruct;
1237 wakaba 1.3 ## NOTE: SHOULD NOT be used to convey machine-readable information.
1238 wakaba 1.2
1239     $Element->{$ATOM_NS}->{source} = {
1240     attrs_checker => $GetAtomAttrsChecker->({}),
1241     checker => sub {
1242     my ($self, $todo) = @_;
1243    
1244     my @nodes = (@{$todo->{node}->child_nodes});
1245     my $new_todos = [];
1246     my $has_element = {};
1247     while (@nodes) {
1248     my $node = shift @nodes;
1249     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1250    
1251     my $nt = $node->node_type;
1252     if ($nt == 1) {
1253     my $nsuri = $node->namespace_uri;
1254     $nsuri = '' unless defined $nsuri;
1255     my $not_allowed;
1256     if ($nsuri eq $ATOM_NS) {
1257     my $ln = $node->manakai_local_name;
1258     if ($ln eq 'entry') {
1259     $has_element->{entry} = 1;
1260     } elsif ({
1261     generator => 1,
1262     icon => 1,
1263     id => 1,
1264     logo => 1,
1265     rights => 1,
1266     subtitle => 1,
1267     title => 1,
1268     updated => 1,
1269     }->{$ln}) {
1270     unless ($has_element->{$ln}) {
1271     $has_element->{$ln} = 1;
1272     $not_allowed = $has_element->{entry};
1273     } else {
1274     $not_allowed = 1;
1275     }
1276     } elsif ($ln eq 'link') {
1277 wakaba 1.3 if ($node->rel eq $LINK_REL . 'alternate') {
1278     my $type = $node->get_attribute_ns (undef, 'type');
1279     $type = '' unless defined $type;
1280     my $hreflang = $node->get_attribute_ns (undef, 'hreflang');
1281     $hreflang = '' unless defined $hreflang;
1282     my $key = 'link:'.(defined $type ? ':'.$type : '').':'.
1283     (defined $hreflang ? ':'.$hreflang : '');
1284     unless ($has_element->{$key}) {
1285     $has_element->{$key} = 1;
1286     } else {
1287     $not_allowed = 1;
1288     }
1289     }
1290     $not_allowed ||= $has_element->{entry};
1291 wakaba 1.2 } elsif ({
1292     author => 1,
1293     category => 1,
1294     contributor => 1,
1295     }->{$ln}) {
1296     $not_allowed = $has_element->{entry};
1297     } else {
1298     $not_allowed = 1;
1299     }
1300     } else {
1301     ## TODO: extension element
1302     $not_allowed = 1;
1303     }
1304     $self->{onerror}->(node => $node, type => 'element not allowed')
1305     if $not_allowed;
1306     my ($sib, $ch) = $self->_check_get_children ($node, $todo);
1307     unshift @nodes, @$sib;
1308     push @$new_todos, @$ch;
1309     } elsif ($nt == 3 or $nt == 4) {
1310     ## TODO: Are white spaces allowed?
1311     $self->{onerror}->(node => $node, type => 'character not allowed');
1312     } elsif ($nt == 5) {
1313     unshift @nodes, @{$node->child_nodes};
1314     }
1315     }
1316 wakaba 1.1
1317     return ($new_todos);
1318     },
1319     };
1320 wakaba 1.2
1321     $Element->{$ATOM_NS}->{subtitle} = $AtomTextConstruct;
1322    
1323     $Element->{$ATOM_NS}->{summary} = $AtomTextConstruct;
1324    
1325     $Element->{$ATOM_NS}->{title} = $AtomTextConstruct;
1326    
1327     $Element->{$ATOM_NS}->{updated} = $AtomDateConstruct;
1328    
1329     ## TODO: signature element
1330    
1331     ## TODO: simple extension element and structured extension element
1332 wakaba 1.1
1333     $Whatpm::ContentChecker::Namespace->{$ATOM_NS}->{loaded} = 1;
1334    
1335     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24