/[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.8 - (hide annotations) (download)
Sun Mar 2 11:16:34 2008 UTC (16 years, 8 months ago) by wakaba
Branch: MAIN
Changes since 1.7: +693 -1019 lines
++ whatpm/t/ChangeLog	2 Mar 2008 11:10:02 -0000
	* content-model-atom-1.dat: New test data are added.

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

++ whatpm/Whatpm/ChangeLog	2 Mar 2008 11:16:26 -0000
	* ContentChecker.pm: Typo fixed.  Don't raise "character encoding"
	and related errors unless it is an HTML document (though the spec
	is unclear on whether it is applied to XHTML document).

	* HTML.pm (%HTMLAttrStatus): WF2 repetition model attributes
	are added.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	2 Mar 2008 11:11:31 -0000
	* Atom.pm: Reimplemented.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24