/[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 - (show 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 package Whatpm::ContentChecker;
2 use strict;
3 require Whatpm::ContentChecker;
4
5 require Whatpm::URIChecker;
6
7 my $ATOM_NS = q<http://www.w3.org/2005/Atom>;
8 my $LINK_REL = q<http://www.iana.org/assignments/relation/>;
9
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 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 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 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 ## 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 ## 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
417 return ($new_todos);
418 },
419 }; # $AtomDateConstruct
420
421 $Element->{$ATOM_NS}->{entry} = {
422 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 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 ## NOTE: MAY
480 $not_allowed ||= $has_element->{entry};
481 } 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 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
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 } 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 $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 unless ($has_element->{'link.self'}) {
637 $self->{onerror}->(node => $todo->{node}, level => 's',
638 type => 'child element missing:atom.link.self');
639 }
640
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 ## 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 }
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 ## 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
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 ## NOTE: SHOULD be suitable for handling as $value.
842 ## If no @src, this would normally mean it contains a
843 ## single child element that would serve as the root element.
844 $self->{onerror}->(node => $todo->{node}, level => 'unsupported',
845 type => 'content:'.$value);
846
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
907 ## NOTE: SHOULD be suitable for the indicated media type.
908 $self->{onerror}->(node => $todo->{node}, level => 'unsupported',
909 type => 'content:'.$value);
910
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 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 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 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 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 my $s = '';
1020 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 $s .= $node->data;
1033 } elsif ($nt == 5) {
1034 unshift @nodes, @{$node->child_nodes};
1035 }
1036 }
1037
1038 ## 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 ## 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
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 hreflang => $AtomLanguageTagAttrChecker,
1106 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 $value = $LINK_REL . $value;
1112 }
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 title => sub { }, # No MUST
1125 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 }),
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 if ($todo->{node}->rel eq $LINK_REL . 'enclosure' and
1165 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
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 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 ## 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 $s .= $node->data;
1215 } elsif ($nt == 5) {
1216 unshift @nodes, @{$node->child_nodes};
1217 }
1218 }
1219
1220 ## 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 ## NOTE: SHOULD NOT be used to convey machine-readable information.
1238
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 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 } 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
1317 return ($new_todos);
1318 },
1319 };
1320
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
1333 $Whatpm::ContentChecker::Namespace->{$ATOM_NS}->{loaded} = 1;
1334
1335 1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24