/[pub]/suikawiki/script/lib/SuikaWiki/Markup/XML.pm
Suika

Contents of /suikawiki/script/lib/SuikaWiki/Markup/XML.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.8 - (hide annotations) (download)
Fri Jun 27 13:07:44 2003 UTC (22 years ago) by wakaba
Branch: MAIN
Changes since 1.7: +78 -31 lines
Some modification to support Parser's external entity support

1 wakaba 1.1
2     =head1 NAME
3    
4     SuikaWiki::Markup::XML --- SuikaWiki: Simple well-formed document fragment generator
5    
6     =head1 DESCRIPTION
7    
8     This module can be used to generate the document fragment of XML, SGML or
9     other well-formed (in XML meaning) data formats with the object oriented manner.
10    
11     This module cannot be used to parse XML (or other marked-up) document (or its fragment)
12     by itself, nor is compatible with other huge packages such as XML::Parser. The only purpose
13     of this module is to make it easy for tiny perl scripts to GENERATE well-formed
14     markup constructures. (SuikaWiki is not "tiny"? Oh, yes, I see:-))
15    
16     =cut
17    
18     package SuikaWiki::Markup::XML;
19     use strict;
20 wakaba 1.8 our $VERSION = do{my @r=(q$Revision: 1.7 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
21 wakaba 1.2 use overload '""' => \&stringify,
22     fallback => 1;
23     use Char::Class::XML qw!InXML_NameStartChar InXMLNameChar InXML_NCNameStartChar InXMLNCNameChar!;
24 wakaba 1.1 our %Namespace_URI_to_prefix = (
25     'DAV:' => [qw/dav webdav/],
26     'http://greenbytes.de/2002/rfcedit' => [qw/ed/],
27     'http://icl.com/saxon' => [qw/saxon/],
28     'http://members.jcom.home.ne.jp/jintrick/2003/02/site-concept.xml#' => ['', qw/sitemap/],
29     'http://purl.org/dc/elements/1.1/' => [qw/dc dc11/],
30     'http://purl.org/rss/1.0/' => ['', qw/rss rss10/],
31     'http://suika.fam.cx/~wakaba/lang/rfc/translation/' => [qw/ja/],
32     'http://www.mozilla.org/xbl' => ['', qw/xbl/],
33 wakaba 1.3 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' => [qw/rdf/],
34 wakaba 1.1 'http://www.w3.org/1999/xhtml' => ['', qw/h h1 xhtml xhtml1/],
35     'http://www.w3.org/1999/xlink' => [qw/l xlink/],
36     'http://www.w3.org/1999/XSL/Format' => [qw/fo xslfo xsl-fo xsl/],
37     'http://www.w3.org/1999/XSL/Transform' => [qw/t s xslt xsl/],
38     'http://www.w3.org/1999/XSL/TransformAlias' => [qw/axslt axsl xslt xsl/],
39     'http://www.w3.org/2000/01/rdf-schema#' => [qw/rdfs/],
40     'http://www.w3.org/2000/svg' => ['', qw/s svg/],
41     'http://www.w3.org/2002/06/hlink' => [qw/h hlink/],
42     'http://www.w3.org/2002/06/xhtml2' => ['', qw/h h2 xhtml xhtml2/],
43 wakaba 1.3 'http://www.w3.org/2002/07/owl' => [qw/owl/],
44 wakaba 1.6 'http://www.w3.org/2002/xforms/cr' => [qw/f xforms/],
45 wakaba 1.1 'http://www.w3.org/TR/REC-smil' => ['', qw/smil smil1/],
46 wakaba 1.3 'http://www.wapforum.org/2001/wml' => [qw/wap/],
47 wakaba 1.1 'http://xml.apache.org/xalan' => [qw/xalan/],
48     'mailto:julian.reschke@greenbytes.de?subject=rcf2629.xslt' => [qw/myns/],
49     'urn:schemas-microsoft-com:vml' => [qw/v vml/],
50     'urn:schemas-microsoft-com:xslt' => [qw/ms msxsl msxslt/],
51     'urn:x-suika-fam-cx:markup:ietf:html:3:draft:00' => ['', qw/H HTML HTML3/],
52 wakaba 1.7 'urn:x-suika-fam-cx:markup:ietf:rfc:2629' => ['', qw/rfc rfc2629/],
53 wakaba 1.1 );
54     my %Cache;
55 wakaba 1.7 my %NS = (
56     SGML => 'urn:x-suika-fam-cx:markup:sgml:',
57 wakaba 1.8 xml => 'http://www.w3.org/XML/1998/namespace',
58     xmlns => 'http://www.w3.org/2000/xmlns/',
59 wakaba 1.7 );
60 wakaba 1.1
61     =head1 METHODS
62    
63     =over 4
64    
65     =item $x = SuikaWiki::Markup::XML->new (%options)
66    
67     Returns new instance of the module. It is itself a node.
68    
69 wakaba 1.4 Available options: C<data_type>, C<default_decl>, C<type> (default: C<#element>), C<local_name>, C<namespace_uri> and C<value>.
70 wakaba 1.1
71     =cut
72    
73     sub new ($;%) {
74     my $class = shift;
75     my $self = bless {@_}, $class;
76     $self->{type} ||= '#element';
77 wakaba 1.5 if ($self->{qname}) {
78     ($self->{namespace_prefix}, $self->{local_name}) = $self->_ns_parse_qname ($self->{qname});
79     $self->{_qname} = $self->{qname};
80     }
81     if (defined $self->{namespace_prefix}) {
82     $self->{namespace_prefix} .= ':' if $self->{namespace_prefix}
83     && substr ($self->{namespace_prefix}, -1) ne ':';
84     $self->{ns}->{$self->{namespace_prefix}||''} = $self->{namespace_uri} if defined $self->{namespace_uri};
85     }
86 wakaba 1.4 for (qw/local_name value/) {
87 wakaba 1.8 if (ref ($self->{$_}) && eval q($self->{$_}->_CLASS_NAME eq $self->_CLASS_NAME)) {
88 wakaba 1.2 $self->{$_}->{parent} = $self;
89     }
90     }
91 wakaba 1.1 $self->{node} ||= [];
92     $self;
93     }
94    
95 wakaba 1.5 sub _ns_parse_qname ($$) {
96     shift;
97     my $qname = shift;
98     if ($qname =~ /:/) {
99     return split /:/, $qname, 2;
100     } else {
101     return (undef, $qname);
102     }
103     }
104 wakaba 1.1
105     =item $x->append_node ($node)
106    
107     Appending given node to the object (as the last child).
108     If the type of given node is C<#fragment>, its all children, not the node
109     itself, are appended.
110    
111     This method returns the appended node unless the type of given node is C<#fragment>.
112     In such cases, this node (C<$x>) is returned.
113    
114 wakaba 1.2 Available options: C<node_or_text>.
115    
116 wakaba 1.1 =cut
117    
118 wakaba 1.2 sub append_node ($$;%) {
119 wakaba 1.1 my $self = shift;
120 wakaba 1.2 my ($new_node, %o) = @_;
121     unless (ref $new_node) {
122     if ($o{node_or_text}) {
123     return $self->append_text ($new_node);
124     } else {
125     die "append_node: Invalid node" unless ref $new_node;
126     }
127     }
128 wakaba 1.1 if ($new_node->{type} eq '#fragment') {
129     for (@{$new_node->{node}}) {
130     push @{$self->{node}}, $_;
131     $_->{parent} = $self;
132     }
133     $self;
134     } else {
135     push @{$self->{node}}, $new_node;
136     $new_node->{parent} = $self;
137     $new_node;
138     }
139     }
140    
141     =item $new_node = $x->append_new_node (%options)
142    
143     Appending a new node. The new node is returned.
144    
145     Available options: C<type>, C<namespace_uri>, C<local_name>, C<value>.
146    
147     =cut
148    
149     sub append_new_node ($;%) {
150     my $self = shift;
151     my %o = @_;
152     my $new_node = __PACKAGE__->new (%o);
153     push @{$self->{node}}, $new_node;
154     $new_node->{parent} = $self;
155     $new_node;
156     }
157    
158     =item $new_node = $x->append_text ($text)
159    
160     Appending given text as a new text node. The new text node is returned.
161    
162     =cut
163    
164     sub append_text ($$;%) {
165     my $self = shift;
166     my $s = shift;
167     #if (@{$self->{node}}[-1]->{type} eq '#text') {
168     # $self->{node}}[-1]->append_new_node (type => '#text', value => $s);
169     #} else {
170     $self->append_new_node (type => '#text', value => $s);
171     #}
172     }
173    
174 wakaba 1.2 sub append_baretext ($$;%) {
175     my $self = shift;
176     my $s = shift;
177     $self->append_new_node (type => '#xml', value => $s);
178     }
179    
180 wakaba 1.1 =item $attr_node = $x->get_attribute ($local_name, %options)
181    
182     Returns the attribute node whose local-name is C<$local_name>.
183    
184     Available options: C<namespace_uri>, C<make_new_node>.
185    
186     =cut
187    
188     sub get_attribute ($$;%) {
189     my $self = shift;
190     my ($name, %o) = @_;
191     for (@{$self->{node}}) {
192     if ($_->{type} eq '#attribute' && $_->{local_name} eq $name && $o{namespace_uri} eq $_->{namespace_uri}) {
193     return $_;
194     }
195     }
196     ## Node is not exist
197     if ($o{make_new_node}) {
198     return $self->append_new_node (type => '#attribute', local_name => $name, namespace_uri => $o{namespace_uri});
199     } else {
200     return undef;
201     }
202     }
203    
204     =item $attr_node = $x->set_attribute ($local_name => $value, %options)
205    
206     Set the value of the attribute. The attribute node is returned.
207    
208     Available options: C<namespace_uri>.
209    
210     =cut
211    
212     sub set_attribute ($$$;%) {
213     my $self = shift;
214     my ($name, $val, %o) = @_;
215 wakaba 1.7 if (ref ($val) eq 'ARRAY' || ref ($val) eq 'HASH' || ref ($val) eq 'CODE') {
216     ## TODO: common error handling
217     require Carp;
218     Carp::croak "set_attribute: new attribute value must be string or blessed object";
219     }
220 wakaba 1.1 for (@{$self->{node}}) {
221     if ($_->{type} eq '#attribute' && $_->{local_name} eq $name && $o{namespace_uri} eq $_->{namespace_uri}) {
222     $_->{value} = $val;
223     $_->{node} = [];
224     return $_;
225     }
226     }
227     return $self->append_new_node (type => '#attribute', local_name => $name, value => $val, namespace_uri => $o{namespace_uri});
228     }
229    
230     =item \@children = $x->child_nodes
231    
232     Returns an array reference to child nodes.
233    
234 wakaba 1.4 =item $local_name = $x->local_name ([$new_name])
235 wakaba 1.1
236 wakaba 1.4 Returns or set the local-name.
237    
238     =item $uri = $x->namespace_uri ([$new_uri])
239    
240     Returns or set namespace name (URI) of the element or the attribute
241 wakaba 1.1
242 wakaba 1.5 =item $uri = $x->namespace_prefix ([$new_prefix])
243    
244     Returns or set namespace prefix of the element or the attribute.
245     You may give C<$new_prefix> in form either 'foo' or 'foo:'.
246     To indicate "default" prefix, use '' (length == 0 string).
247    
248 wakaba 1.1 =item $type = $x->node_type
249    
250     Returns the node type.
251    
252     =item $node = $x->parent_node
253    
254     Returns the parent node. If there is no parent node, undef is returned.
255    
256     =cut
257    
258     sub child_nodes ($) { shift->{node} }
259 wakaba 1.4 sub local_name ($;$) {
260     my ($self, $newname) = @_;
261     if ($newname) {
262     $self->{local_name} = $newname;
263     }
264     if (ref $self->{local_name} && $self->{local_name}->{type} eq '#declaration') {
265     $self->{local_name}->{local_name};
266     } else {
267     $self->{local_name}
268     }
269     }
270 wakaba 1.1 sub node_type ($) { shift->{type} }
271     sub parent_node ($) { shift->{parent} }
272    
273 wakaba 1.4 ## TODO: obsolete
274     sub target_name ($;$) {
275     my ($self, $new) = @_;
276     if (defined $new) {
277     $self->{target_name} = $new;
278     }
279     $self->{target_name};
280     }
281    
282     sub namespace_uri ($;$) {
283     my ($self, $new_uri) = @_;
284     if (defined $new_uri) {
285     $self->{namespace_uri} = $new_uri;
286     }
287     $self->{namespace_uri};
288     }
289 wakaba 1.5 sub namespace_prefix ($;$) {
290     my ($self, $new_pfx) = @_;
291     if (defined $new_pfx && $self->{namespace_uri}) {
292     $new_pfx .= ':' if $new_pfx;
293     $self->{namespace_prefix} = $new_pfx;
294     $self->{ns}->{$new_pfx} = $self->{namespace_uri};
295     }
296     $self->_get_namespace_prefix ($self->{namespace_uri});
297     }
298 wakaba 1.4
299 wakaba 1.1 =item $i = $x->count
300    
301     Returns the number of child nodes.
302    
303     =cut
304    
305     # TODO: support counting by type
306     sub count ($;@) {
307 wakaba 1.2 my $self = shift;
308     (defined $self->{value} ? 1 : 0) + scalar @{$self->{node}};
309 wakaba 1.1 }
310    
311     # $prefix = $x->_get_namespace_prefix ($namespace_uri)
312     sub _get_namespace_prefix ($$;%) {
313     my ($self, $uri) = (shift, shift);
314     my %o = @_;
315     if (defined (my $p = $self->_uri_to_prefix ($uri, undef, %o))) {
316     return $p if $self->_prefix_to_uri ($p) eq $uri;
317     } if ($Namespace_URI_to_prefix{$uri}) {
318     for (@{$Namespace_URI_to_prefix{$uri}}) {
319     my $pfx = $_; $pfx .= ':' if $pfx;
320     if ($self->_check_namespace_prefix ($pfx) && !$self->_prefix_to_uri ($pfx)) {
321     return $self->_uri_to_prefix ($uri => $pfx, %o);
322     }
323     }
324     } else {
325     my ($u_r_i, $pfx) = ($uri);
326 wakaba 1.4 $u_r_i =~ s/[^0-9A-Za-z._-]+/ /g;
327     my @u_r_i = split / /, $u_r_i;
328     for (reverse @u_r_i) {
329     if (s/([A-Za-z][0-9A-Za-z._-]+)//) {
330     my $p_f_x = $1 . ':';
331     next if lc (substr ($p_f_x, 0, 3)) eq 'xml';
332     unless ($self->_prefix_to_uri ($p_f_x)) {
333     $pfx = $p_f_x;
334     last;
335     }
336 wakaba 1.1 }
337     }
338     if ($pfx) {
339     return $self->_uri_to_prefix ($uri => $pfx, %o);
340     } else {
341     while (1) {
342     my $pfx = 'ns'.(++$self->{ns}->{-anonymous}).':';
343     unless ($self->_prefix_to_uri ($pfx)) {
344     return $self->_uri_to_prefix ($uri => $pfx, %o);
345     }
346     }
347     }
348     }
349     }
350    
351 wakaba 1.4 sub _set_prefix_to_uri ($$$;%) {
352     my ($self, $prefix => $uri, %o) = @_;
353     return undef unless $self->_check_namespace_prefix ($prefix);
354     $self->{ns}->{$prefix} = $uri;
355     $self->_prefix_to_uri ($prefix);
356     }
357    
358     ## TODO: removing ns declare (1.1) support
359     # $uri or undef = $x->_prefix_to_uri ($prefix)
360 wakaba 1.1 sub _prefix_to_uri ($$;$%) {
361 wakaba 1.4 my ($self, $prefix, %o) = @_;
362 wakaba 1.1 return undef unless $self->_check_namespace_prefix ($prefix);
363     if (uc (substr $prefix, 0, 3) eq 'XML') {
364 wakaba 1.8 return $NS{xml} if $prefix eq 'xml:';
365     return $NS{xmlns} if $prefix eq 'xmlns:';
366 wakaba 1.2 }
367     if (defined $self->{ns}->{$prefix}) {
368 wakaba 1.1 $self->{ns}->{$prefix};
369     } elsif (ref $self->{parent}) {
370     shift; # $self
371     $self->{parent}->_prefix_to_uri (@_);
372     } else {
373     undef;
374     }
375     }
376    
377     # $prefix or undef = $x->_uri_to_prefix ($uri => [$new_prefix], %options)
378     # use_no_prefix (default: 1): Allow default namespace (no prefix).
379     sub _uri_to_prefix ($$;$%) {
380     my ($self, $uri, $new_prefix, %o) = @_;
381     if (defined $new_prefix && $self->_check_namespace_prefix ($new_prefix)) {
382     $self->{ns}->{$new_prefix} = $uri;
383     $new_prefix;
384     } else {
385 wakaba 1.8 return 'xml:' if $uri eq $NS{xml};
386     return 'xmlns:' if $uri eq $NS{xmlns};
387 wakaba 1.1 for (keys %{$self->{ns}||{}}) {
388     next if ($_ eq '') && !(!defined $o{use_no_prefix} || $o{use_no_prefix});
389     return $_ if $self->{ns}->{$_} eq $uri;
390     }
391 wakaba 1.2 if (ref ($self->{parent}) && $self->{parent}->{type} ne '#declaration') {
392 wakaba 1.1 shift; # $self
393     $self->{parent}->_uri_to_prefix (@_);
394     } else {
395     undef;
396     }
397     }
398     }
399    
400     =item $x->define_new_namespace ($prefix => $uri)
401    
402     Defines a new XML Namespace. This method is useful for root or section-level
403     element node.
404    
405     Returned value is unspecified in this version of this module.
406    
407     =cut
408    
409 wakaba 1.4 ## TODO: structured URI (such as http://&server;/) support
410 wakaba 1.1 sub define_new_namespace ($$$) {
411     my ($self, $prefix, $uri) = @_;
412     if ($prefix eq '' || $self->_check_ncname ($prefix)) {
413     $prefix .= ':' if $prefix && substr ($prefix, -1) ne ':';
414 wakaba 1.4 $self->_set_prefix_to_uri ($prefix => $uri);
415 wakaba 1.1 } else {
416     undef;
417     }
418     }
419    
420 wakaba 1.4 =item $uri = $x->defined_namespace_prefix ($prefix)
421    
422     Query whether the namespace prefix is defined or not.
423     If defined, return namespace name (URI).
424    
425     =cut
426    
427     sub defined_namespace_prefix ($$) {
428     my ($self, $prefix) = @_;
429     $prefix .= ':' if $prefix;#Carp::carp join ",", ';',@_;
430     $self->_prefix_to_uri ($prefix);
431     }
432    
433 wakaba 1.1 =item $qname = $x->qname
434    
435     Returns QName ((namespace-)qualified name) of the element type.
436     Undef is retuened when the type does not have its QName
437     (ie. when type is neither C<#element> or C<#attribute>).
438    
439     =cut
440    
441     sub qname ($) {
442     my $self = shift;
443     if ($self->_check_ncname ($self->{local_name})) {
444     if ($self->{type} eq '#element') {
445     $self->{_qname} = $self->_get_namespace_prefix ($self->{namespace_uri}) . $self->{local_name}
446     unless $self->{_qname};
447     return $self->{_qname};
448     } elsif ($self->{type} eq '#attribute') {
449     return $self->attribute_name;
450     }
451     }
452     undef;
453     }
454    
455 wakaba 1.7 sub remove_references ($) {
456     my $self = shift;
457     my @node;
458 wakaba 1.8 for (@{$self->{node}}) {
459     $_->remove_references;
460     }
461 wakaba 1.7 for (@{$self->{node}}) {
462     if ($_->{type} ne '#reference'
463     || ($self->{type} eq '#declaration' && $_->{namespace_uri} eq $NS{SGML}.'entity')) {
464     push @node, $_;
465     } else {
466     if ($_->{namespace_uri} =~ /char/) {
467     my $e = ref ($_)->new (type => '#text', value => chr $_->{value});
468     $e->{parent} = $self;
469     push @node, $e;
470     } elsif ($_->{flag}->{smxp__ref_expanded}) {
471     for my $e (@{$_->{node}}) {
472 wakaba 1.8 if ($e->{type} ne '#attribute') {
473     $e->{parent} = $self;
474     push @node, $e;
475     }
476 wakaba 1.7 }
477     } else { ## reference is not expanded
478     push @node, $_;
479     }
480     }
481 wakaba 1.8 $_->{flag}->{smxp__defined_with_param_ref} = 0
482     if $_->{flag}->{smxp__defined_with_param_ref};
483 wakaba 1.7 }
484     $self->{node} = \@node;
485     }
486    
487 wakaba 1.8 sub resolve_relative_uri ($;$%) {
488     require URI;
489     my ($self, $rel, %o) = @_;
490     my $base = $self->get_attribute ('base', namespace_uri => $NS{xml});
491     $base = ref ($base) ? $base->inner_text : undef;
492     if ($base !~ /^(?:[0-9A-Za-z.+-]|%[0-9A-Fa-f]{2})+:/) { # $base is relative
493     $base = $self->_resolve_relative_uri_by_parent ($base, \%o);
494     }
495     eval q{ ## Catch error such as $base is 'data:,foo' (non hierarchic scheme,...)
496     return URI->new ($rel)->abs ($base || '.'); ## BUG (or spec) of URI: $base == false
497     } or return $rel;
498     }
499     sub _resolve_relative_uri_by_parent ($$$) {
500     my ($self, $rel, $o) = @_;
501     if (ref $self->{parent}) {
502     if (!$o->{use_references_base_uri} && $self->{parent}->{type} eq '#reference') {
503     ## This case is necessary to work with
504     ## <element> <!-- element can have base URI -->
505     ## text <!-- text cannot have base URI -->
506     ## &ent; <!-- ref's base URI is referred entity's one (in this module) -->
507     ## <!-- expantion of ent -->
508     ## entity's text <!-- text cannot have base URI, so use <element>'s one -->
509     ## <entitys-element/> <!-- element can have base URI, otherwise ENTITY's one -->
510     ## </element>
511     return $self->{parent}->_resolve_relative_uri_by_parent ($rel, $o);
512     } else {
513     return $self->{parent}->resolve_relative_uri ($rel, %$o);
514     }
515     } else {
516     return $rel;
517     }
518     }
519     sub base_uri ($;$) {
520     my ($self, $new_uri) = @_;
521     my $base;
522     if (defined $new_uri) {
523     $base = $self->set_attribute (base => $new_uri, namespace_uri => $NS{xml});
524     }
525     $base ||= $self->get_attribute ('base', namespace_uri => $NS{xml});
526     ref ($base) ? $base->inner_text : undef;
527     }
528    
529 wakaba 1.1 =item $tag = $x->start_tag
530    
531 wakaba 1.4 Returns the start tag (or something that marks the start of something, such as '<!--'
532 wakaba 1.1 for C<#comment> nodes).
533    
534     =cut
535    
536     sub start_tag ($) {
537     my $self = shift;
538     if ($self->{type} eq '#element' && $self->_check_ncname ($self->{local_name})) {
539     my $r = '<';
540     $r .= $self->qname;
541     for (@{$self->{node}}) {
542     $r .= ' ' . $_->outer_xml if $_->node_type eq '#attribute';
543     }
544     for my $prefix (grep !/^-/, keys %{$self->{ns}||{}}) {
545     if ($prefix) {
546     $r .= ' xmlns:'.substr ($prefix, 0, length ($prefix)-1);
547     } else {
548     $r .= ' xmlns';
549     }
550     $r .= '="'.$self->_entitize ($self->{ns}->{$prefix}).'"';
551     }
552     $r .= '>';
553     $r;
554     } elsif ($self->{type} eq '#comment') {
555 wakaba 1.4 '<!--';
556     } elsif ($self->{type} eq '#pi' && $self->_check_ncname ($self->{local_name})) {
557     '<?' . ($self->{local_name});
558 wakaba 1.2 } elsif ($self->{type} eq '#reference') {
559     if ($self->{namespace_uri} eq 'urn:x-suika-fam-cx:markup:sgml:char:ref:hex') {
560     '&#x';
561     } elsif ($self->{namespace_uri} eq 'urn:x-suika-fam-cx:markup:sgml:char:ref') {
562     '&#';
563 wakaba 1.4 } elsif (ref $self->{local_name} && $self->{local_name}->{type} eq '#declaration') {
564     if ($self->{local_name}->{namespace_uri} eq 'urn:x-suika-fam-cx:markup:sgml:entity:parameter') {
565 wakaba 1.2 '%';
566     } else {
567     '&';
568     }
569 wakaba 1.4 } elsif ($self->_check_ncname ($self->{local_name})) {
570 wakaba 1.2 if ($self->{namespace_uri} eq 'urn:x-suika-fam-cx:markup:sgml:entity:parameter') {
571     '%';
572     } else {
573     '&';
574     }
575     } else { # error
576     '';
577     }
578 wakaba 1.4 } elsif ($self->{type} eq '#declaration' && $self->{namespace_uri}) {
579     '<!' . {
580 wakaba 1.8 $NS{SGML}.'attlist' => 'ATTLIST',
581     $NS{SGML}.'doctype' => 'DOCTYPE',
582     $NS{SGML}.'element' => 'ELEMENT',
583     $NS{SGML}.'entity' => 'ENTITY',
584     $NS{SGML}.'entity:parameter' => 'ENTITY',
585     $NS{SGML}.'notation' => 'NOTATION',
586 wakaba 1.7 }->{$self->{namespace_uri}} . ' ' . ($self->{namespace_uri} eq $NS{SGML}.'entity:parameter' ?
587     ($self->{flag}->{smxp__defined_with_param_ref}?'':'% '):'');
588 wakaba 1.1 } elsif ($self->{type} eq '#declaration' && $self->_check_ncname ($self->{local_name})) {
589     my $r = '<!' . $self->{local_name} . ' ';
590     if ($self->{local_name} eq 'DOCTYPE' && ref $self->{parent}) {
591     my $qname;
592     for (@{$self->{parent}->{node}}) {
593     if ($_->{type} eq '#element') {
594     $qname = $_->qname;
595     last if $qname;
596     }
597     }
598     $r .= ($qname ? $qname : '#IMPLIED') . ' ';
599 wakaba 1.2 }
600     $r;
601     } elsif ($self->{type} eq '#section') {
602     if (ref $self->{local_name} && $self->{local_name}->{type} eq '#reference') {
603     '<![' . $self->{local_name} . '[';
604     } elsif ($self->_check_ncname ($self->{local_name})) {
605     '<![' . $self->{local_name} . '[';
606     } else { # error
607     '';
608 wakaba 1.1 }
609     } else {
610     '';
611     }
612     }
613    
614     =item $tag = $x->end_tag
615    
616 wakaba 1.4 Returns the end tag (or something that marks the end of something, such as '-->'
617 wakaba 1.1 for C<#comment> nodes).
618    
619     =cut
620    
621     sub end_tag ($) {
622     my $self = shift;
623     if ($self->{type} eq '#element' && $self->_check_ncname ($self->{local_name})) {
624     '</' . $self->qname . '>';
625     } elsif ($self->{type} eq '#comment') {
626 wakaba 1.4 '-->';
627 wakaba 1.1 } elsif ($self->{type} eq '#pi' && $self->_check_ncname ($self->{local_name})) {
628     '?>';
629 wakaba 1.2 } elsif ($self->{type} eq '#reference') {
630     ';';
631 wakaba 1.4 } elsif ($self->{type} eq '#declaration' && $self->{namespace_uri}) {
632     '>';
633 wakaba 1.1 } elsif ($self->{type} eq '#declaration' && $self->_check_ncname ($self->{local_name})) {
634 wakaba 1.4 '>';
635 wakaba 1.2 } elsif ($self->{type} eq '#section') {
636     if (ref $self->{local_name} && $self->{local_name}->{type} eq '#reference') {
637     ']]>';
638     } elsif ($self->_check_ncname ($self->{local_name})) {
639     ']]>';
640     } else { # error
641     '';
642     }
643 wakaba 1.1 } else {
644     '';
645     }
646     }
647    
648     =item $tag = $x->attribute_name
649    
650     Returns the attribute name.
651    
652     =cut
653    
654     sub attribute_name ($) {
655     my $self = shift;
656     if ($self->{type} eq '#attribute' && $self->_check_ncname ($self->{local_name})) {
657     ($self->{namespace_uri} ?
658     (ref $self->{parent} ? $self->{parent} : $self)
659     ->_get_namespace_prefix ($self->{namespace_uri}, use_no_prefix => 0) : '')
660     .$self->{local_name};
661     } else {
662     '';
663     }
664     }
665    
666     =item $tag = $x->attribute_value
667    
668     Returns the attribute value.
669    
670     =cut
671    
672 wakaba 1.4 sub attribute_value ($;%) {
673 wakaba 1.1 my $self = shift;
674 wakaba 1.4 my %o = @_;
675 wakaba 1.1 if ($self->{type} eq '#attribute' && $self->_check_ncname ($self->{local_name})) {
676 wakaba 1.5 my $r = '"';
677 wakaba 1.8 if (ref ($self->{value}) && eval q($self->{value}->_CLASS_NAME eq $self->_CLASS_NAME)) {
678 wakaba 1.5 unshift @{$self->{node}}, $self->{value};
679     undef $self->{value};
680     } else {
681     $r .= $self->_entitize ($self->{value});
682     }
683 wakaba 1.4 for (@{$self->{node}}) {
684     my $nt = $_->node_type;
685     if ($nt eq '#reference' || $nt eq '#xml') {
686     $r .= $_->outer_xml;
687     } elsif ($nt ne '#attribute') {
688     $r .= $self->_entitize ($_->inner_text, percent => $o{escape_percent});
689     }
690     }
691     return $r . '"';
692     } else {
693     '';
694     }
695     }
696    
697     sub entity_value ($;%) {
698     my $self = shift;
699     my %o = @_;
700     my $_entitize = sub {
701     my $s = shift;
702 wakaba 1.7 $s =~ s/&/&#x26;/g;
703     $s =~ s/&#x26;(\p{InXML_NameStartChar}\p{InXMLNameChar}*);/&$1;/g;
704 wakaba 1.4 $s =~ s/%/&#x25;/g;
705 wakaba 1.7 $s =~ s/"/&#x22;/g;
706 wakaba 1.4 $s;
707     };
708     if ($self->{type} eq '#attribute' && $self->_check_ncname ($self->{local_name})) {
709     my $r = '"' . &$_entitize ($self->{value});
710     for (@{$self->{node}}) {
711     my $nt = $_->node_type;
712     if ($nt eq '#reference' || $nt eq '#xml') {
713     $r .= $_->outer_xml;
714     } elsif ($nt ne '#attribute') {
715     $r .= &$_entitize ($_->inner_text);
716 wakaba 1.1 }
717     }
718 wakaba 1.4 return $r . '"';
719 wakaba 1.1 } else {
720     '';
721     }
722     }
723    
724 wakaba 1.7 ## This method should be called only from SuikaWiki::Markup::XML::* family modules,
725     ## since this is NOT a FORMAL interface.
726     sub _entity_parameter_literal_value ($;%) {
727     my $self = shift;
728     my $r = $self->{value};
729     for (@{$self->{node}}) {
730     my $nt = $_->node_type;
731 wakaba 1.8 #if ($nt eq '#reference' || $nt eq '#xml') {
732     if ($nt eq '#xml') {
733 wakaba 1.7 $r .= $_->outer_xml;
734     } elsif ($nt ne '#attribute') {
735     $r .= $_->inner_text;
736     }
737     }
738     return $r;
739     }
740    
741 wakaba 1.1 =item $tag = $x->attribute
742    
743     Returns the attribute (name and value pair).
744    
745     =cut
746    
747     sub attribute ($) {
748     my $self = shift;
749     if ($self->{type} eq '#attribute' && $self->_check_ncname ($self->{local_name})) {
750     $self->attribute_name . '=' . $self->attribute_value;
751     } else {
752     '';
753     }
754     }
755    
756 wakaba 1.2 sub external_id ($;%) {
757     my $self = shift;
758     my %o = @_;
759     my ($pubid, $sysid, $ndata);
760     for (@{$self->{node}}) {
761 wakaba 1.4 if ($_->{type} eq '#attribute' && !$_->{namespace_uri}) {
762 wakaba 1.2 if ($_->{local_name} eq 'PUBLIC') {
763 wakaba 1.4 $pubid = $_->inner_text;
764 wakaba 1.2 } elsif ($_->{local_name} eq 'SYSTEM') {
765 wakaba 1.4 $sysid = $_->inner_text;
766 wakaba 1.2 } elsif ($_->{local_name} eq 'NDATA') {
767 wakaba 1.4 $ndata = $_->inner_text;
768     undef $ndata unless $self->_check_ncname ($ndata);
769 wakaba 1.2 }
770     }
771     }
772     my $r = '';
773 wakaba 1.4 if (length $pubid) {
774     $pubid =~ s|([^\x0A\x0D\x20A-Za-z0-9'()+,./:=?;!*#\@\$_%-])|sprintf '%%%02X', ord $1|ges;
775     }
776     if (length $sysid) {
777     if ($sysid =~ /"/) {
778     if ($sysid =~ /'/) {
779     $sysid =~ s/"/&quot;/; $sysid = '"' . $sysid . '"';
780     } else {
781     $sysid = "'" . $sysid . "'";
782     }
783     } else {
784     $sysid = '"' . $sysid . '"';
785     }
786     }
787 wakaba 1.2 if ($pubid && $sysid) {
788 wakaba 1.4 $r = 'PUBLIC "' . $pubid . '" ' . $sysid;
789 wakaba 1.2 } elsif ($sysid) {
790 wakaba 1.4 $r = 'SYSTEM ' . $sysid;
791 wakaba 1.2 } elsif ($pubid && $o{allow_pubid_only}) {
792 wakaba 1.4 $r = 'PUBLIC "' . $pubid . '"';
793 wakaba 1.2 }
794     if ($r && $ndata && $o{use_ndata}) {
795 wakaba 1.4 $r .= ' NDATA ' . $ndata;
796 wakaba 1.2 }
797     $r;
798     }
799    
800     =item $s = $x->content_spec
801    
802     Generates contentspec of element type declaration (ex. C<(E1 | E2 | E3)>)
803     or AttDef of attribute declaration (ex. C<name CDATA #REQUIRED>).
804    
805     =cut
806    
807     sub content_spec ($) {
808     my $self = shift;
809     if ($self->{type} eq '#element') {
810     my $text = 0;
811     my $contentspec = join ' | ', map {$_->qname} grep {$text = 1 if $_->{type} eq '#text'; $_->{type} eq '#element'} @{$self->{node}};
812     $contentspec = '#PCDATA' . ($contentspec ? ' | ' . $contentspec : '') if $text;
813    
814     return $contentspec ? '(' . $contentspec . ')' : 'EMPTY';
815     } elsif ($self->{type} eq '#attribute') {
816     my $attdef = $self->qname . "\t" . ($self->{data_type} || 'CDATA') . "\t";
817     my $default = $self->{default_decl};
818     $default .= ' ' . $self->attribute_value if $default eq '#FIXED';
819     unless ($default) {
820     $default = defined $self->{value} ? $self->attribute_value : '#IMPLIED';
821     }
822     return $attdef . $default;
823     }
824     }
825    
826 wakaba 1.1 =item $tag = $x->inner_xml
827    
828     Returns the content of the node in XML syntax. (In case of the C<#element> nodes,
829     element content without start- and end-tags is returned.)
830    
831     Note that for not all node types the behavior of this method is defined.
832     For example, returned value of C<#attribute> might be unexpected one
833     in this version of this module.
834    
835     =cut
836    
837 wakaba 1.3 sub inner_xml ($;%) {
838 wakaba 1.1 my $self = shift;
839 wakaba 1.3 my %o = @_;
840 wakaba 1.1 my $r = '';
841     if ($self->{type} eq '#comment') {
842     $r = $self->inner_text;
843     $r =~ s/--/-&#x45;/g;
844     } elsif ($self->{type} eq '#pi') {
845     if (length $self->{value}) {
846     $r = ' ' . $self->{value};
847 wakaba 1.2 $r =~ s/\?>/? >/g; # Same replacement as of the recommendation of XSLT:p.i.
848 wakaba 1.1 }
849     for (@{$self->{node}}) {
850     if ($_->node_type eq '#attribute') {
851     $r .= ' ' . $_->attribute;
852     } else {
853     my $s = $_->inner_text;
854     $s =~ s/\?>/?&gt;/g;
855     $r .= ' ' . $s if length $s;
856     }
857     }
858 wakaba 1.2 } elsif ($self->{type} eq '#reference') {
859     if ($self->{namespace_uri} eq 'urn:x-suika-fam-cx:markup:sgml:char:ref:hex') {
860     $r = sprintf '%02X', $self->{value};
861     } elsif ($self->{namespace_uri} eq 'urn:x-suika-fam-cx:markup:sgml:char:ref') {
862     $r = sprintf '%02d', $self->{value};
863 wakaba 1.4 } elsif (ref $self->{local_name} && $self->{local_name}->{type} eq '#declaration') {
864     $r = $self->{local_name}->{local_name};
865     } elsif ($self->_check_ncname ($self->{local_name})) {
866     $r = ($self->{local_name});
867 wakaba 1.2 } else { # error
868     $r = '';
869     }
870 wakaba 1.1 } elsif ($self->{type} eq '#declaration') {
871 wakaba 1.8 if ($self->{namespace_uri} eq $NS{SGML}.'doctype') {
872 wakaba 1.4 my $root = $self->get_attribute ('qname');
873     ref $root ? ($root = $root->inner_text) : (ref $self->{parent} ? (do {
874     for (@{$self->{parent}->{node}}) {
875     if ($_->{type} eq '#element') {
876     $root = $_->qname;
877     last if $root;
878     }
879     }
880     $root = '#IMPLIED';
881     }) : ($root = '#IMPLIED')); ## error!
882     my ($isub, $xid) = ('', $self->external_id);
883     for (@{$self->{node}}) {
884     $isub .= $_->outer_xml if $_->{type} ne '#attribute';
885 wakaba 1.2 }
886     if ($xid) {
887 wakaba 1.4 $r = $xid;
888     if ($isub) {
889 wakaba 1.7 $r .= " [" . $isub . "]";
890 wakaba 1.4 }
891     } else {
892     if ($isub) {
893 wakaba 1.7 $r = "[" . $isub . "]";
894 wakaba 1.4 } else {
895     $r = "[]";
896     }
897     }
898     $r = $root . ' ' . $r;
899     } elsif ($self->{namespace_uri} eq 'urn:x-suika-fam-cx:markup:sgml:entity'
900     || $self->{namespace_uri} eq 'urn:x-suika-fam-cx:markup:sgml:entity:parameter'
901     || $self->{namespace_uri} eq 'urn:x-suika-fam-cx:markup:sgml:notation') {
902     my %xid_opt;
903 wakaba 1.7 $r = $self->{local_name} . ' ' if !$self->{flag}->{smxp__defined_with_param_ref}
904     && $self->_check_ncname ($self->{local_name});
905 wakaba 1.8 if ($self->{namespace_uri} eq $NS{SGML}.'entity:parameter') {
906 wakaba 1.4 #$r = '% ' . $r;
907 wakaba 1.8 } elsif ($self->{namespace_uri} eq $NS{SGML}.'entity') {
908 wakaba 1.4 $xid_opt{use_ndata} = 1;
909 wakaba 1.8 } elsif ($self->{namespace_uri} eq $NS{SGML}.'notation') {
910 wakaba 1.4 $xid_opt{allow_pubid_only} = 1;
911     }
912    
913     my ($v, $xid) = ($self->{value}, $self->external_id (%xid_opt));
914 wakaba 1.7 undef $xid if $self->{flag}->{smxp__defined_with_param_ref};
915 wakaba 1.4 if ($xid) { ## External ID
916 wakaba 1.2 $r .= $xid;
917 wakaba 1.4 } else { ## EntityValue
918     my $entity_value = $self->get_attribute ('value');
919 wakaba 1.7 undef $entity_value if $self->{flag}->{smxp__defined_with_param_ref};
920 wakaba 1.4 if ($entity_value) { # <!ENTITY foo "bar">
921     $r .= $entity_value->entity_value;
922     } else { ## Parameter entity reference
923     $r .= $self->{value};
924     for (@{$self->{node}}) {
925 wakaba 1.7 $r .= $_->outer_xml unless $_->{type} eq '#attribute';
926 wakaba 1.4 }
927 wakaba 1.2 }
928 wakaba 1.1 }
929 wakaba 1.8 } elsif ($self->{namespace_uri} eq $NS{SGML}.'element') {
930 wakaba 1.4 my $qname = $self->get_attribute ('qname');
931     ref $qname ? $qname = $qname->inner_text : undef;
932     if ($qname && $self->_check_name ($qname)) {
933     ## Element type name is defined
934     $r = $qname . ' ';
935 wakaba 1.3 if ($o{output_tag_omit_declaration}) {
936 wakaba 1.4 $r .= ($self->{flag}->{element_tag_start_omitable} ? 'o ' : '- ')
937     .
938     ($self->{flag}->{element_tag_end_omitable} ? 'o ' : '- ');
939 wakaba 1.3 }
940 wakaba 1.4 my $rs = $self->inner_text (output_ref_as_is => 1);
941     $r .= $rs || 'ANY';
942 wakaba 1.2 } else {
943 wakaba 1.4 #if (ref $self->{node}->[0] && $self->{node}->[0]->{type} eq '#element') {
944     ### Element prototype is given
945     # $r = $self->{node}->[0]->qname . ' ' . &$to . $self->{node}->[0]->content_spec;
946     #} elsif () {
947     ### Element type name and contentspec is given
948     # $r = $self->{target_name} . ' ' . &$to . ($self->inner_text || 'ANY');
949     #} else {
950 wakaba 1.2 ## (Element type name and contetnspac) is given
951 wakaba 1.4 $r .= $self->inner_text (output_ref_as_is => 1);
952     # || 'Name ANY'; # error
953     #}
954 wakaba 1.2 }
955 wakaba 1.7 ## TODO: reform
956 wakaba 1.2 } elsif ($self->{local_name} eq 'ATTLIST') {
957     if ($self->_check_name ($self->{target_name})) {
958     $r = $self->{target_name};
959     }
960     my $t = $self->inner_text (output_ref_as_is => 1);
961     $r .= "\n\t" . $t if $t;
962     $r ||= 'Name'; # error!
963     for (@{$self->{node}}) {
964     if ($_->{type} eq '#attribute') {
965     $r .= "\n\t" . $_->content_spec;
966     }
967 wakaba 1.1 }
968 wakaba 1.2 } else { # unknown
969 wakaba 1.3 for (@{$self->{node}}) {
970     $r .= $_->outer_xml;
971     }
972 wakaba 1.1 }
973 wakaba 1.2 } elsif ($self->{type} eq '#section' && !ref $self->{local_name} && $self->{local_name} eq 'CDATA') {
974     $r = $self->inner_text;
975     $r =~ s/]]>/]]>]]<![CDATA[>/g;
976 wakaba 1.1 } else {
977     if ($self->{type} ne '#xml') {
978     $r = $self->_entitize ($self->{value});
979     } else {
980     $r = $self->{value};
981     }
982     for (@{$self->{node}}) {
983     my $nt = $_->node_type;
984 wakaba 1.4 if (($self->{option}->{indent}) && ($nt eq '#element' || $nt eq '#comment' || $nt eq '#pi' || $nt eq '#declaration')) {
985 wakaba 1.1 $r .= "\n";
986     }
987     $r .= $_->outer_xml unless $_->node_type eq '#attribute';
988     }
989     }
990     $r;
991     }
992    
993    
994     =item $tag = $x->outer_xml
995    
996     Returns the node in XML syntax.
997    
998     =cut
999    
1000     sub outer_xml ($) {
1001     my $self = shift;
1002     if ($self->{type} eq '#attribute') {
1003     $self->attribute;
1004     } else {
1005 wakaba 1.4 if ($self->{option}->{indent} && $self->{type} eq '#element') {
1006 wakaba 1.1 my $r = $self->start_tag;
1007     my $c = $self->inner_xml;
1008 wakaba 1.4 if (!length $c && $self->{option}->{use_EmptyElemTag}) {
1009     substr ($r, -1) = ' />';
1010     } else {
1011     if ($c) {
1012     $c =~ s/\n/\n /g;
1013     $r .= "\n " . $c . "\n";
1014     }
1015     $r .= $self->end_tag;
1016 wakaba 1.1 }
1017     $r;
1018     } else {
1019 wakaba 1.4 my $r = $self->start_tag;
1020     my $c = $self->inner_xml;
1021 wakaba 1.8 if ($self->{type} eq '#element' && !length ($c) && $self->{option}->{use_EmptyElemTag}) {
1022 wakaba 1.4 substr ($r, -1) = ' />';
1023     } else {
1024     $r .= $c . $self->end_tag;
1025     #$r .= "\n" if $self->{type} eq '#declaration';
1026     }
1027 wakaba 1.2 $r;
1028     #'{'.$self->{type}.': '.$r.'}'; # for debug
1029 wakaba 1.1 }
1030     }
1031     }
1032    
1033     =item $tag = $x->inner_text
1034    
1035     Returns the text content of the node. (In many case the returned value is same
1036     as WinIE DOM C<inner_text ()> function's or XPath C<text()> function's.
1037     But some classes that inherits this module might implement to return other
1038     value (eg. to return the value of the alt attribute of html:img element).
1039    
1040 wakaba 1.2 Available options: C<output_ref_as_is>.
1041    
1042 wakaba 1.1 =cut
1043    
1044 wakaba 1.2 sub inner_text ($;%) {
1045 wakaba 1.1 my $self = shift;
1046 wakaba 1.2 my %o = @_;
1047 wakaba 1.5 my $r = '';
1048 wakaba 1.7 if ($self->{type} eq '#reference' && #) {
1049     #if (
1050 wakaba 1.8 ($self->{namespace_uri} eq $NS{SGML}.'char:ref' ||
1051     $self->{namespace_uri} eq $NS{SGML}.'char:ref:hex')) {
1052 wakaba 1.7 $r = chr $self->{value};
1053     #} else { # general entity ref. or parameter entity ref.
1054     # ## TODO: how implement? is this ok?
1055     # my $em = $self->_get_entity_manager;
1056     # $r = $em->get_entity_value ($self->{local_name}, namespace_uri => $self->{namespace_uri});
1057     #}
1058     } elsif ($self->{type} eq '#declaration'
1059     && ($self->{namespace_uri} eq $NS{SGML}.'entity'
1060     || $self->{namespace_uri} eq $NS{SGML}.'entity:parameter')) {
1061     ## TODO:
1062     $r = $self->get_attribute ('value')->inner_text;
1063     } else { # not #reference nor #declaration(ENTITY)
1064 wakaba 1.8 if (ref ($self->{value}) && eval q($self->{value}->_CLASS_NAME eq $self->_CLASS_NAME)) {
1065 wakaba 1.7 unshift @{$self->{node}}, $self->{value};
1066     undef $self->{value};
1067     } else {
1068     $r = $self->{value};
1069     }
1070     if ($o{output_ref_as_is}) { ## output as if RCDATA
1071     $r =~ s/&/&amp;/g;
1072     for my $node (@{$self->{node}}) {
1073     my $nt = $node->node_type;
1074     if ($nt eq '#reference' || $nt eq '#xml') {
1075     $r .= $node->outer_xml;
1076     } elsif ($nt ne '#attribute') {
1077     $r .= map {s/&/&amp;/g; $_} $node->inner_text;
1078     }
1079     }
1080     } else {
1081     for (@{$self->{node}}) {
1082     $r .= $_->inner_text unless $_->node_type eq '#attribute';
1083 wakaba 1.2 }
1084     }
1085 wakaba 1.1 }
1086     $r;
1087     }
1088    
1089     {no warnings; # prototype mismatch
1090     *stringify = \&outer_xml;
1091     }
1092    
1093 wakaba 1.7 sub _get_entity_manager ($) {
1094     my $self = shift;
1095     if ($self->{type} eq '#document') {
1096     unless ($self->{flag}->{smx__entity_manager}) {
1097     require SuikaWiki::Markup::XML::EntityManager;
1098     $self->{flag}->{smx__entity_manager} = SuikaWiki::Markup::XML::EntityManager->new ($self);
1099     }
1100     return $self->{flag}->{smx__entity_manager};
1101     } elsif (ref $self->{parent}) {
1102     return $self->{parent}->_get_entity_manager;
1103     } else {
1104     unless ($self->{flag}->{smx__entity_manager}) {
1105     require SuikaWiki::Markup::XML::EntityManager;
1106     $self->{flag}->{smx__entity_manager} = SuikaWiki::Markup::XML::EntityManager->new ($self);
1107     }
1108     return $self->{flag}->{smx__entity_manager};
1109     }
1110     }
1111    
1112 wakaba 1.5 sub _CLASS_NAME ($) {
1113     'SuikaWiki::Markup::XML';
1114     }
1115    
1116 wakaba 1.1 # $s = $x->_entitize ($s)
1117 wakaba 1.4 sub _entitize ($$;%) {
1118     my ($self, $s, %o) = (shift, shift, @_);
1119 wakaba 1.1 $s =~ s/&/&amp;/g;
1120     $s =~ s/</&lt;/g;
1121     $s =~ s/>/&gt;/g;
1122     $s =~ s/"/&quot;/g;
1123 wakaba 1.4 $s =~ s/%/&#x25;/g if $o{percent};
1124     $s =~ s/'/&#x27;/g if $o{apos};
1125 wakaba 1.7 $s =~ s/([\x00-\x08\x0B\x0C\x0E-\x1F])/sprintf '&amp;#%d;', ord $1/ge;
1126 wakaba 1.1 $s;
1127     }
1128    
1129 wakaba 1.2 # 1/0 = $x->_check_name ($s)
1130     sub _check_name ($$) {
1131     my $self = shift;
1132     my $s = shift;
1133     return $Cache{name}->{$s} if defined $Cache{name}->{$s};
1134     if ($s =~ /^\p{InXML_NameStartChar}/ && $s !~ /\P{InXMLNameChar}/) {
1135     # \p{...}('*'/'+'/'{n,}') does not work...
1136     $Cache{name}->{$s} = 1;
1137     1;
1138     } else {
1139     $Cache{name}->{$s} = 0;
1140     0;
1141     }
1142     }
1143 wakaba 1.1 # 1/0 = $x->_check_ncname ($s)
1144     sub _check_ncname ($$) {
1145     my $self = shift;
1146     my $s = shift;
1147     return $Cache{ncname}->{$s} if defined $Cache{ncname}->{$s};
1148     if ($s =~ /^\p{InXML_NCNameStartChar}/ && $s !~ /\P{InXMLNCNameChar}/) {
1149     # \p{...}('*'/'+'/'{n,}') does not work...
1150     $Cache{ncname}->{$s} = 1;
1151     1;
1152     } else {
1153     $Cache{ncname}->{$s} = 0;
1154     0;
1155     }
1156     }
1157    
1158     # 1/0 = $x->_check_namespace_prefix ($s)
1159     sub _check_namespace_prefix ($$) {
1160     my $self = shift;
1161     my $s = shift;
1162     return 0 unless defined $s;
1163     return 1 if $s eq '';
1164     substr ($s, -1, 1) = '' if substr ($s, -1, 1) eq ':';
1165     $self->_check_ncname ($s);
1166     }
1167    
1168 wakaba 1.2 ## TODO: cleaning $self->{node} before outputing, to ensure nodes not to have
1169     ## multiple parents.
1170     ## TODO: normalize namespace URI (removing non URI chars)
1171    
1172     sub flag ($$;$) {
1173     my ($self, $name, $value) = @_;
1174     if (defined $value) {
1175     $self->{flag}->{$name} = $value;
1176     }
1177     $self->{flag}->{$name};
1178     }
1179    
1180 wakaba 1.3 sub option ($$;$) {
1181     my ($self, $name, $value) = @_;
1182     if (defined $value) {
1183     $self->{option}->{$name} = $value;
1184     }
1185     $self->{option}->{$name};
1186     }
1187    
1188 wakaba 1.1 =back
1189    
1190 wakaba 1.2 =head1 NODE TYPES
1191    
1192     =over 4
1193    
1194     =item #attribute
1195    
1196     Attribute. Its XML representation takes the form of NAME="VALUE".
1197    
1198     =item #comment
1199    
1200     Comment declarement. <!-- -->
1201    
1202     =item #declarement
1203    
1204     SGML's declarements, such as SGML, DOCTYPE, ENTITY, etc.
1205     <!SGML ...>, <!DOCTYPE root []>, <!ENTITY % name "value">
1206    
1207     =item #element
1208    
1209     Element. Its XML representation consists of start tag, content and end tag,
1210     like <TYPE>content</TYPE>.
1211    
1212     =item #fragment
1213    
1214     Fragment of nodes. It's similar to DOM's fragment node.
1215    
1216     =item #pi
1217    
1218     Prosessing instruction. <?NAME VALUE?>
1219    
1220     =item #reference
1221    
1222     Character reference or general or parameter entity reference.
1223     &#nnnn;, &#xhhhh;, &name;, %name;.
1224    
1225     =item #section
1226    
1227     Markup section. CDATA, INCLUDE and IGNORE are supported by XML.
1228     <![%type;[...]]>
1229    
1230     =item #text
1231    
1232     Text.
1233    
1234     =item #xml
1235    
1236     Preformatted XML text.
1237    
1238     =cut
1239    
1240 wakaba 1.4 =head1 RESTRICTIONS
1241    
1242     =over 4
1243    
1244     =item XML without XML Namespace is not supported.
1245    
1246     =item Before default namespace without bounded URI (xmlns="") is outputed, it must be declared.
1247    
1248     For example, next code generates invalid (non-well-formed) XML Namespace document.
1249    
1250     my $x = SuikaWiki::Markup::XML->new (local_name => 'elementType');
1251     print $x # <ns1:elementType xmlns:ns1=""></ns1:elementType>
1252    
1253     So you must write like:
1254    
1255     my $x = SuikaWiki::Markup::XML->new (local_name => 'elementType');
1256     $x->define_new_namespace ('' => '');
1257     print $x; # <elementType xmlns=""></elementType>
1258    
1259     =back
1260    
1261 wakaba 1.1 =head1 LICENSE
1262    
1263     Copyright 2003 Wakaba <w@suika.fam.cx>
1264    
1265     This program is free software; you can redistribute it and/or
1266     modify it under the same terms as Perl itself.
1267    
1268     =cut
1269    
1270 wakaba 1.8 1; # $Date: 2003/06/16 09:59:49 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24