/[suikacvs]/markup/tool/mkdtds.pl
Suika

Contents of /markup/tool/mkdtds.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (show annotations) (download)
Tue Jan 13 11:17:20 2004 UTC (20 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.2: +237 -64 lines
File MIME type: text/plain
Use new version of SuikaWikiConfig/2.0 parser

1 #!/usr/bin/perl
2 use strict;
3 our $SCRIPT_NAME = 'mkdtds';
4 our $VERSION = do{my @r=(q$Revision: 1.26 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
5 {require Message::Markup::SuikaWikiConfig20::Parser;
6
7 my $parser = new Message::Markup::SuikaWikiConfig20::Parser;
8 local $/ = undef;
9 my $src = $parser->parse_text (scalar <>);
10 my $Info = {};
11
12 for my $src ($src->get_attribute ('ModuleSet')
13 || $src->get_attribute ('DocumentType')) {
14 for (qw/ID Copyright BaseURI Description Version/) {
15 $Info->{$_} = $src->get_attribute_value ($_);
16 }
17 $Info->{Name} = $src->get_attribute_value ('Name');
18 $Info->{Name} .= ' ' . $Info->{Version} if length $Info->{Version};
19 $Info->{ns} = $src->get_attribute ('Namespace');
20 }
21
22 for (@{$src->child_nodes}) {
23 if ($_->local_name eq 'Attribute') {
24 attrib_module ($_, $Info);
25 } elsif ($_->local_name eq 'Datatype') {
26 datatype_module ($_, $Info);
27 } elsif ($_->local_name eq 'Notation') {
28 notation_module ($_, $Info);
29 } elsif ($_->local_name eq 'Module') {
30 submodule ($_, $Info);
31 } elsif ($_->local_name eq 'Model') {
32 model_module ($_, $Info);
33 $Info->{has_model} = 1;
34 } elsif ($_->local_name eq 'Driver') {
35 dtd_driver ($_, $Info);
36 }
37 }
38
39 if (ref $src->get_attribute ('ModuleSet')) {
40 qname_module ($src->get_attribute ('ModuleSet'), $Info);
41 }
42 exit}
43
44
45 sub make_paragraphs ($;%) {
46 my ($para, %opt) = @_;
47 join "\n\n", map {
48 my $s = $_;
49 $s =~ s/\n+$//g;
50 $s =~ s/\n/\n$opt{indent}/g;
51 $opt{indent}.$s;
52 } grep {length} @$para;
53 }
54
55 sub dot_padding ($%) {
56 my ($s, %opt) = @_;
57 if ($opt{length} - length $s > 0) {
58 return $s . ( ($opt{dot} or q(.)) x ($opt{length} - length $s) );
59 } else {
60 return $s;
61 }
62 }
63
64 sub submodule_id_of ($$;%) {
65 my ($src, $Info, %opt) = @_;
66 my $id = $src->get_attribute_value ('ID') || $opt{default};
67 unless ($id) {
68 die "$0: Submodule identifier not specified";
69 }
70 $id;
71 }
72 sub xml_datatype_of ($$;%) {
73 my ($src, $Info, %opt) = @_;
74 my $type = $src->get_attribute_value ('XMLType') || $opt{default};
75 $type =~ s/\s+//g;
76 $type;
77 }
78 sub system_id_of ($$;%) {
79 my ($src, $Info, %opt) = @_;
80 my $sysid = $src->get_attribute_value ('SYSTEM');
81 if ($sysid =~ /<([^>]+)>/) {
82 return $1;
83 } else {
84 return $opt{base}.($sysid || $opt{default});
85 }
86 }
87 sub external_id_of ($$;%) {
88 my ($src, $Info, %opt) = @_;
89 my $sysid = system_id_of ($src, $Info, %opt);
90 my $pubid = $src->get_attribute_value ('PUBLIC');
91 if ($pubid) {
92 if ($sysid) {
93 return qq(PUBLIC "$pubid"\n\t "$sysid");
94 } else {
95 return qq(PUBLIC "$pubid");
96 }
97 } else {
98 return qq(SYSTEM "$sysid");
99 }
100 }
101 sub name_of ($$;%) {
102 my ($src, $Info, %opt) = @_;
103 unless (ref $src) {require Carp; Carp::croak ('$src undefined')}
104 my $name = $src->get_attribute_value ($opt{key} || 'Name');
105 if ($name =~ /^:(.+)/) { ## Global namespace
106 return $1;
107 } elsif ($name =~ /([^:]+):(.+)/) { ## Named space
108 return $1.($opt{delim}||'.').$2;
109 } else { ## Default namespace
110 return $Info->{ID}.($opt{delim}||'.').$name;
111 }
112 }
113 sub local_name_of ($$;%) {
114 my ($src, $Info, %opt) = @_;
115 my $name = $src->get_attribute_value ($opt{key} || 'Name');
116 if ($name =~ /^:(.+)/) { ## Global namespace
117 return $1;
118 } elsif ($name =~ /[^:]+:(.+)/) { ## Named space
119 return $1;
120 } else { ## Default namespace
121 return $name;
122 }
123 }
124 sub set_name_of ($$;%) {
125 my ($src, $Info, %opt) = @_;
126 my $name = $src->get_attribute_value ($opt{key} || 'Name');
127 if ($name =~ /^:.+/) { ## Global namespace
128 return 'XHTML';
129 } elsif ($name =~ /([^:]+):.+/) { ## Named space
130 return $1;
131 } else { ## Default namespace
132 return $Info->{ID};
133 }
134 }
135 sub class_name_of ($$;%) {
136 my ($src, $Info, %opt) = @_;
137 my $name = name_of ($src, $Info, %opt);
138 unless ($name =~ /\.(class|mix|content|datatype)$/) {
139 $name .= '.class';
140 }
141 $name;
142 }
143 sub convert_content_model ($$;%) {
144 my ($src, $Info, %opt) = @_;
145 my $model = $src->get_attribute_value ($opt{key} || 'Content') || $opt{default};
146 $model =~ s/\s//g;
147 my $nonsymbol = qr/[^%#?,\$;()+*|:]/;
148 $model =~ s/(?<![%#.])((?:\$|\b)$nonsymbol+(?::$nonsymbol+)?|\$?:$nonsymbol+|"[^"]+")/get_model_token ($1, $Info)/ge;
149 $model;
150 }
151 sub sparalit ($) {
152 my $s = paralit (shift);
153 $s =~ s/&/&#x26;/g;
154 $s =~ s/%/&#x25;/g;
155 $s;
156 }
157 sub paralit ($) {
158 my $s = shift;
159 if ($s =~ /"/) {
160 if ($s =~ /'/) {
161 $s =~ s/'/&#x27;/g;
162 return qq("$s");
163 } else {
164 return qq('$s');
165 }
166 } else {
167 return qq("$s");
168 }
169 }
170 sub description ($$;%) {
171 my ($src, $Info, %opt) = @_;
172 my $desc = $src->get_attribute_value ('Description');
173 $desc =~ s/\n/\n /g;
174 unless ($desc) {
175 $desc = {
176 load_module => {
177 AttributeModule => q/Common Attributes Module/,
178 DatatypeModule => q/Datatypes Module/,
179 NotationModule => q/Notation Module/,
180 QNameModule => q/QName Module/,
181 },
182 }->{$opt{context}}->{$opt{id} || $src->get_attribute_value ($opt{id_key}||'ID')};
183 }
184 $desc = qq(<!-- $desc -->\n) if $desc;
185 $desc;
186 }
187 sub xml_condition_section ($$;%) {
188 my ($condition, $content, %opt) = @_;
189 qq(<![%$condition;[\n)
190 . $content
191 . qq(<!-- end of $condition -->]]>\n);
192 }
193 sub xml_parameter_ENTITY ($%) {
194 my ($name, %opt) = @_;
195 qq(<!ENTITY % $name @{[paralit $opt{value}]}>\n);
196 }
197
198 sub entity_declaration ($$;%) {
199 my ($src, $Info, %opt) = @_;
200 my $val;
201 if ($src->get_attribute_value ('ID')
202 || $src->get_attribute_value ('SYSTEM')
203 || $src->get_attribute_value ('PUBLIC')) {
204 $val = "\n\t".external_id_of ($src, $Info, default => $src->get_attribute_value ('ID'));
205 } elsif (ref $src->get_attribute ('Declaration')) {
206 $val = "\n\t".sparalit submodule_declarations ($src->get_attribute ('Declaration'), $Info);
207 } else {
208 $val = paralit $src->get_attribute_value ('EntityValue');
209 }
210 my $s = <<EOH;
211 @{[description ($src, $Info)]}<!ENTITY @{[$opt{param}?'% ':'']}@{[$src->get_attribute_value ('Name')]} $val>
212
213 EOH
214 $s;
215 }
216
217 sub dtd_driver ($$) {
218 my ($src, $Info) = @_;
219 my $s = '';
220 my %s;
221 my @module_set;
222 for my $src (@{$src->child_nodes}) {
223 if ($src->local_name eq 'Module') {
224 $s .= dtd_driver_load_module ($src, $Info);
225 } elsif ($src->local_name eq 'DTD') {
226 $s .= dtd_driver_load_dtd ($src, $Info);
227 } elsif ($src->local_name eq 'ModuleSet') {
228 push @module_set, $src;
229 } elsif ($src->local_name =~ /^(?:QName|Attribute|Datatype|Notation)Module/) {
230 $s{$src->local_name} .= dtd_driver_load_module ($src, $Info);
231 } elsif ($src->local_name eq 'IfModuleSet') {
232 $s .= qq(<![%@{[$src->get_attribute_value ('ModuleSet')]}.module;[\n);
233 $s .= submodule_declarations ($src, $Info);
234 $s .= qq(]]>\n);
235 } elsif ($src->local_name eq 'GeneralEntity') {
236 $s .= entity_declaration ($src, $Info, param => 0);
237 } elsif ($src->local_name eq 'ParameterEntity') {
238 $s .= entity_declaration ($src, $Info, param => 1);
239 }
240 }
241
242 $s{ModelModule} = $src->get_attribute_value ('NoModelModule') ? '' :
243 $Info->{has_model} ? <<EOH : '';
244 <!-- Document Model module -->
245 <!ENTITY % $Info->{ID}-model.module "INCLUDE">
246 <![%$Info->{ID}-model.module;[
247 <!ENTITY % $Info->{ID}-model.decl
248 'SYSTEM "$Info->{ID}-model.mod"'>
249 <!ENTITY % $Info->{ID}-model.mod %$Info->{ID}-model.decl;>
250 %$Info->{ID}-model.mod;]]>
251
252 EOH
253
254 $s = dtd_driver_module_sets (\@module_set, $Info)
255 . $s{QNameModule}.$s{DatatypeModule}.$s{NotationModule}.$s{AttributeModule}
256 . $s{ModelModule}
257 .$s;
258 make_dtd ($src, $Info, $src->get_attribute_value ('ID'), $s);
259 }
260
261 sub dtd_driver_module_sets ($$) {
262 my ($srces, $Info) = @_;
263 my @src = map {{src => $_}} @$srces;
264 my $s = qq(<!-- Switchers to include/ignore each vocabulary -->\n);
265 for my $module_set (@src) {
266 $module_set->{ID} = $module_set->{src}->get_attribute_value ('ID') || 'XHTML';
267 $s .= qq(<!ENTITY % $module_set->{ID}.module "@{[$module_set->{src}->get_attribute_value ('Default') > 0 ? 'INCLUDE' : 'IGNORE']}">\n);
268 }
269 $s .= qq(\n<!-- Namespace names -->\n);
270 for my $module_set (@src) {
271 $module_set->{ns} = $module_set->{src}->get_attribute ('Namespace');
272 $s .= qq(<!ENTITY % $module_set->{ID}.xmlns "@{[$module_set->{ns}->get_attribute_value ('Name')]}">\n);
273 }
274 $s .= qq(\n<!-- Base URIs for the relavant DTD modules -->\n);
275 for my $module_set (@src) {
276 $s .= qq(<!ENTITY % $module_set->{ID}.sysid.base "@{[$module_set->{src}->get_attribute_value ('BaseURI')]}">\n);
277 }
278 $s .= qq(\n<!-- Namespace prefix -->\n);
279 $s .= qq(<!ENTITY % NS.prefixed "@{[$Info->{ns}->get_attribute_value ('UsePrefix') > 0 ? 'INCLUDE' : 'IGNORE']}">\n);
280 for my $module_set (@src) {
281 $s .= qq(<!ENTITY % $module_set->{ID}.prefix "@{[$module_set->{ns}->get_attribute_value ('DefaultPrefix')]}">\n);
282 $s .= qq(<!ENTITY % $module_set->{ID}.prefixed "@{[$module_set->{ns}->get_attribute_value ('UsePrefix') > 0 ? 'INCLUDE' : $module_set->{ns}->get_attribute_value ('UsePrefix') < 0 ? 'IGNORE' : '%NS.prefixed;']}">\n);
283 }
284 $s .= qq(\n<!-- a URI reference -->\n<!ENTITY % URI.datatype "CDATA">\n);
285 $s .= qq(\n<!-- Placefolders for foreign namespace declarations -->\n);
286 for my $module_set (@src) {
287 $s .= qq(<!ENTITY % $module_set->{ID}.xmlns.extra.attrib "">\n);
288 }
289 $s .= qq(\n<!-- Namespace declarations -->\n);
290 for my $module_set (@src) {
291 $s .= qq(<![%$module_set->{ID}.module;[
292 <![%$module_set->{ID}.prefixed;[
293 <!ENTITY % $module_set->{ID}.xmlns.decl.attrib
294 "xmlns:%$module_set->{ID}.prefix; %URI.datatype; #FIXED '%$module_set->{ID}.xmlns;'">
295 ]]>
296 <!ENTITY % $module_set->{ID}.xmlns.decl.attrib
297 "xmlns %URI.datatype; #FIXED '%$module_set->{ID}.xmlns;'">
298 ]]>
299 <!ENTITY % $module_set->{ID}.xmlns.decl.attrib "">\n\n);
300 }
301 $s .= qq(\n<!ENTITY % NS.decl.attrib
302 ").join ("\n\t", (map {qq(%$_->{ID}.xmlns.decl.attrib;)} @src),
303 map {qq(%$_->{ID}.xmlns.extra.attrib;)} @src).qq(">\n);
304 $s .= qq(\n);
305 for my $module_set (@src) {
306 $s .= qq(<!ENTITY % $module_set->{ID}.xmlns.attrib "%NS.decl.attrib;">\n);
307 }
308 $s .= qq(\n\n);
309 $s;
310 }
311
312 sub dtd_driver_load_module ($$) {
313 my ($src, $Info) = @_;
314 my $module_name = name_of ($src, $Info, key => 'ID');
315 my $module_hyphen_name = name_of ($src, $Info, key => 'ID', delim => '-');
316 my $module_set_name = set_name_of ($src, $Info, key => 'ID');
317 my $module_id = local_name_of ($src, $Info, key => 'ID');
318
319 my $s .= <<EOH;
320 @{[description ($src, $Info, context => 'load_module', id => $src->local_name)]}<![%$module_set_name.module;[
321 <!ENTITY % $module_name.module "@{[$src->get_attribute_value ('Default') >= 0 ? 'INCLUDE' : 'IGNORE']}">
322 <![%$module_name.module;[
323 @{[submodule_declarations ($src, $Info)]}<!ENTITY % $module_name.decl
324 @{[paralit external_id_of ($src, $Info, default => qq($module_hyphen_name.mod), base => qq(%$module_set_name.sysid.base;))]}>
325 <!ENTITY % $module_name.mod %$module_name.decl;>
326 %$module_name.mod;]]>
327 ]]>
328
329 EOH
330 $s;
331 }
332
333 sub dtd_driver_load_dtd ($$) {
334 my ($src, $Info) = @_;
335 my $module_set_name = $src->get_attribute_value ('ID');
336
337 my $s .= <<EOH;
338 @{[description ($src, $Info)]}<![%$module_set_name.module;[
339 @{[submodule_declarations ($src, $Info)]}<!ENTITY % $module_set_name.dtd.sysid "@{[system_id_of ($src, $Info, default => $src->get_attribute_value ('ID').'.dtd', base => qq(%$module_set_name.sysid.base;))]}">
340 @{[do{
341 my $pubid = $src->get_attribute_value ('PUBLIC');
342 if ($pubid) {
343 qq(<!ENTITY % $module_set_name.dtd.fpi "$pubid">\n<!ENTITY % $module_set_name.dtd.fpi.defined "INCLUDE">\n);
344 } else {
345 qq(<!ENTITY % $module_set_name.dtd.fpi "">\n<!ENTITY % $module_set_name.dtd.fpi.defined "IGNORE">\n);
346 }
347 }]}
348 <![%$module_set_name.dtd.fpi.defined;[
349 <!ENTITY % $module_set_name.dtd.decl
350 'PUBLIC "%$module_set_name.dtd.fpi;"
351 "%$module_set_name.dtd.sysid;"'>
352 ]]>
353 <!ENTITY % $module_set_name.dtd.decl
354 'SYSTEM "%$module_set_name.dtd.sysid;"'>
355 <!ENTITY % $module_set_name.dtd %$module_set_name.dtd.decl;>
356 %$module_set_name.dtd;]]>
357
358 EOH
359 $s;
360 }
361
362 sub model_module ($$) {
363 my ($src, $Info) = @_;
364 my $s = '';
365 for my $src (@{$src->child_nodes}) {
366 if ($src->local_name eq 'Class') {
367 $s .= qq(@{[description ($src, $Info)]}<!ENTITY % @{[class_name_of ($src, $Info)]} @{[paralit convert_content_model ($src, $Info)]}>\n\n);
368 } elsif ($src->local_name eq 'Content') {
369 $s .= element_content_def ($src, $Info);
370 }
371 }
372 make_module ($src, $Info, submodule_id_of ($src, $Info, default => 'model'), $s);
373 }
374
375 sub datatype_module ($$) {
376 my ($src, $Info) = @_;
377 my $s = '';
378 for my $src (@{$src->child_nodes}) {
379 if ($src->local_name eq 'Type') {
380 $s .= qq(@{[get_desc ($src, $Info)]}<!ENTITY % @{[name_of ($src, $Info)]}.datatype "@{[xml_datatype_of ($src, $Info, default => 'CDATA')]}">\n\n);
381 }
382 }
383 make_module ($src, $Info, submodule_id_of ($src, $Info, default => 'datatype'), $s);
384 }
385
386 sub notation_module ($$) {
387 my ($src, $Info) = @_;
388 my $s = '';
389 for my $src (@{$src->child_nodes}) {
390 if ($src->local_name eq 'Notation') {
391 $s .= qq(@{[get_desc ($src, $Info)]}<!NOTATION @{[name_of ($src, $Info)]} @{[external_id_of ($src, $Info)]}>\n\n);
392 }
393 }
394 make_module ($src, $Info, submodule_id_of ($src, $Info, default => 'notation'), $s);
395 }
396
397 sub qname_module ($$) {
398 my ($src, $Info) = @_;
399 my $ID = $Info->{ID};
400 my $ns = $src->get_attribute ('Namespace');
401 my $s = <<EOH;
402 <!ENTITY % NS.prefixed "@{[$ns->get_attribute_value ('UsePrefix')==1?
403 q(INCLUDE):q(IGNORE)]}">
404
405 <!-- Section A: XML Namespace Framework :::::::::::::::::::::::::: -->
406
407 <!-- 1. Declare conditional section keyword, used to activate namespace
408 prefixing. -->
409 <!ENTITY % $ID.prefixed "@{[$ns->get_attribute_value ('UsePrefix')==1?
410 q(INCLUDE):
411 $ns->get_attribute_value ('UsePrefix')==-1?
412 q(IGNORE):
413 q(%NS.prefixed;)]}">
414
415 <!-- 2. Declare a parameter entity containing the namespace name. -->
416 <!ENTITY % $ID.xmlns "@{[$ns->get_attribute_value ('Name')]}">
417
418 <!-- 3. Declare parameter entities containing the default namespace prefix
419 string to use when prefixing is enabled. -->
420 <!ENTITY % $ID.prefix "@{[$ns->get_attribute_value ('DefaultPrefix')]}">
421
422 <!-- 4. Declare parameter entities containing the colonized prefix
423 used when prefixing is active, an empty string when it is not. -->
424 <![%$ID.prefixed;[
425 <!ENTITY % $ID.pfx "%$ID.prefix;:">
426 ]]>
427 <!ENTITY % $ID.pfx "">
428
429 <!-- declare qualified name extensions here -->
430 <!ENTITY % ${ID}-qname-extra.mod "">
431 %${ID}-qname-extra.mod;
432
433 <!-- 5. This parameter entity may be redeclared to contain any foreign
434 namespace declaration attributes for namespaces embedded. -->
435 <!ENTITY % $ID.xmlns.extra.attrib "">
436
437 <![%$ID.prefixed;[
438 <!ENTITY % $ID.xmlns.decl.attrib
439 "xmlns:%$ID.prefix; %URI.datatype; #FIXED '%$ID.xmlns;'">
440 ]]>
441 <!ENTITY % $ID.xmlns.decl.attrib
442 "xmlns %URI.datatype; #FIXED '%$ID.xmlns;'">
443
444 <![%$ID.prefixed;[
445 <!ENTITY % NS.decl.attrib
446 "%$ID.xmlns.decl.attrib;
447 %$ID.xmlns.extra.attrib;">
448 ]]>
449 <!ENTITY % NS.decl.attrib
450 "%$ID.xmlns.extra.attrib;">
451
452 <!-- Declare a parameter entity containing all XML namespace declaration
453 attributes used, including a default xmlns declaration when prefixing
454 is inactive. -->
455 <![%$ID.prefixed;[
456 <!ENTITY % $ID.xmlns.attrib
457 "%NS.decl.attrib;">
458 ]]>
459 <!ENTITY % $ID.xmlns.attrib
460 "%$ID.xmlns.decl.attrib;
461 %NS.decl.attrib;">
462
463 <!-- Section B: Qualified Names :::::::::::::::::::::::::::::::::: -->
464
465 <!-- placeholder for qualified name redeclarations -->
466 <!ENTITY % ${ID}-qname-extra.mod "">
467 %${ID}-qname-extra.mod;
468
469 <!-- 6. Declare parameter entities used to provide namespace-qualified
470 names for all element types and global attribute names. -->
471 EOH
472 for my $lname (sort keys %{$Info->{QName}}) {
473 $s .= qq(<!ENTITY % )
474 . (dot_padding qq($Info->{ID}.$lname.qname),
475 length => 15 + length ($Info->{ID}), dot => ' ')
476 . qq( "%$Info->{ID}.pfx;$lname">\n);
477 }
478 $s .= qq(\n);
479 for my $lname (sort keys %{$Info->{QNameA}}) {
480 $s .= qq(<!ENTITY % )
481 . (dot_padding qq($Info->{ID}.$lname.attrib.qname),
482 length => 15 + length ($Info->{ID}), dot => ' ')
483 . qq( "%$Info->{ID}.prefix;:$lname">\n);
484 }
485 $s .= qq(\n);
486 for my $lname (sort keys %{$Info->{QNameB}}) {
487 $s .= qq(<!ENTITY % )
488 . (dot_padding qq($Info->{ID}.$lname.attribute.qname),
489 length => 15 + length ($Info->{ID}), dot => ' ')
490 . qq( "%$Info->{ID}.pfx;$lname">\n);
491 }
492 make_module ($src->get_attribute ('QName', make_new_node => 1), $Info, 'qname', $s);
493 }
494
495 sub get_name ($$;$) {
496 my ($src, $Info, $key) = @_;
497 my $name = $src->get_attribute_value ($key || 'Name');
498 if ($name =~ /^:(.+)/) {
499 $name = $1;
500 } elsif ($name =~ /([^:]+):(.+)/) {
501 $name = qq($1.$2);
502 } else {
503 $name = qq($Info->{ID}.$name);
504 }
505 $name;
506 }
507
508 sub get_qname ($$) {
509 my ($src, $Info) = @_;
510 my $name = $src->get_attribute_value ('Name');
511 if ($name =~ /"([^"]+)"/) {
512 $name = qq($1);
513 } elsif ($name =~ /^:(.+)/) {
514 $name = qq(%$1.qname;);
515 } elsif ($name =~ /([^:]+):(.+)/) {
516 $name = qq(%$1.$2.qname;);
517 } elsif ($name =~ /\{([^{}]+)\}/) {
518 $Info->{QNameB}->{$1} = 1;
519 $name = qq(%$Info->{ID}.$1.attribute.qname;);
520 } else {
521 $Info->{QNameA}->{$name} = 1;
522 $name = qq(%$Info->{ID}.$name.attrib.qname;);
523 }
524 $name;
525 }
526
527 sub get_atype ($$) {
528 my ($src, $Info) = @_;
529 my $name = $src->get_attribute_value ('Type');
530 if ($name =~ /^:(.+)/) {
531 $name = qq(%$1.datatype;);
532 } elsif ($name =~ /([^:]+):(.+)/) {
533 $name = qq(%$1.$2.datatype;);
534 } elsif ($name =~ /"([^"]+)"/) {
535 $name = qq($1);
536 } else {
537 $name = qq(%$Info->{ID}.$name.datatype;);
538 }
539 $name;
540 }
541
542 sub get_adefault ($$) {
543 my ($src, $Info) = @_;
544 my $name = $src->get_attribute_value ('Default');
545 if (defined $name) {
546 } else {
547 $name = qq(#IMPLIED);
548 }
549 $name;
550 }
551
552 sub get_desc ($$;%) {
553 my ($src, $Info, %opt) = @_;
554 my $desc = $src->get_attribute_value ('Description');
555 $desc =~ s/\n/\n /g;
556 if (length $desc) {
557 $desc = qq($opt{prefix}$desc);
558 $desc .= q( ) if $opt{padding_length};
559 $desc = q(<!-- ).(dot_padding $desc, length => $opt{padding_length},
560 dot => $opt{padding_dot}).qq( -->\n);
561 }
562 $desc;
563 }
564
565 sub attset_def ($$) {
566 my ($src, $Info) = @_;
567 my $name = get_name ($src, $Info);
568 my $s .= qq(@{[get_desc ($src, $Info)]}<!ENTITY % $name.attrib\n\t);
569 my @s;
570 if ($name eq qq($Info->{ID}.common)) {
571 push @s, qq(%$Info->{ID}.common.extra.attrib;);
572 push @s, qq(%$Info->{ID}.xmlns.attrib;);
573 }
574 for my $src (@{$src->child_nodes}) {
575 ## Attribute Definition
576 if ($src->local_name eq 'Attribute') {
577 push @s, attrib_def ($src, $Info);
578 ## Reference to Attribute Definition
579 } elsif ($src->local_name eq 'ref') {
580 push @s, attrib_ref ($src, $Info);
581 } elsif ($src->local_name eq 'REF') {
582 push @s, attrib_REF ($src, $Info);
583 }
584 }
585 $s .= paralit join "\n\t", @s;
586 $s .= qq(>\n\n);
587 $s;
588 }
589
590 sub attrib_module ($$) {
591 my ($src, $Info) = @_;
592 my $s = <<EOH;
593 <!ENTITY % $Info->{ID}.common.extra.attrib "">
594
595 EOH
596 my $output_common = 0;
597 for my $src (@{$src->child_nodes}) {
598 ## Attributes Set
599 if ($src->local_name eq 'Attribute' or $src->local_name eq 'AttributeSet') {
600 $s .= attset_def ($src, $Info);
601 $output_common = 1 if get_name ($src, $Info) eq qq($Info->{ID}.common);
602 }
603 }
604 unless ($output_common) {
605 $s .= <<EOH;
606 <!ENTITY % $Info->{ID}.common.attrib
607 "%$Info->{ID}.common.extra.attrib;
608 %$Info->{ID}.xmlns.attrib;">
609
610 EOH
611 }
612 make_module ($src, $Info, ($src->get_attribute_value ('ID') || 'attribs'), $s);
613 }
614
615 sub attrib_def ($$) {
616 my ($src, $Info) = @_;
617 my $s = qq(@{[get_qname ($src, $Info)]} @{[get_atype ($src, $Info)]} @{[get_adefault ($src, $Info)]});
618 $s;
619 }
620
621 sub attrib_ref ($$) {
622 my ($src, $Info) = @_;
623 my $name = $src->value;
624 if ($name =~ /^:(.+)/) {
625 $name = $1;
626 } elsif ($name =~ /([^:]+):(.+)/) {
627 $name = qq($1.$2);
628 } else {
629 $name = qq($Info->{ID}.$name);
630 }
631 qq(%$name.attrib;);
632 }
633
634 sub attrib_REF ($$) {
635 my ($src, $Info) = @_;
636 {
637 'xml:base' => q<xml:base %URI.datatype; #IMPLIED>,
638 'xml:lang' => q<xml:lang %LanguageCode.datatype; #IMPLIED>,
639 'xml:space' => q<xml:space (default|preserve) #IMPLIED>,
640 }->{$src->value};
641 }
642
643 sub submodule ($$) {
644 my ($src, $Info) = @_;
645 local $Info->{elements} = [];
646 my $s = submodule_declarations ($src, $Info);
647 make_module ($src, $Info, $src->get_attribute_value ('ID'), $s);
648 }
649
650 sub submodule_declarations ($$) {
651 my ($src, $Info) = @_;
652 my $s = '';
653 for my $src (@{$src->child_nodes}) {
654 if ($src->local_name eq 'Element') {
655 $s .= element_def ($src, $Info);
656 } elsif ($src->local_name eq 'Attribute') {
657 $s .= attlist_def ($src, $Info);
658 } elsif ($src->local_name eq 'AttributeSet') {
659 $s .= attset_def ($src, $Info);
660 } elsif ($src->local_name eq 'Class') {
661 $s .= qq(@{[description ($src, $Info)]}<!ENTITY % @{[class_name_of ($src, $Info)]} @{[paralit convert_content_model ($src, $Info)]}>\n\n);
662 } elsif ($src->local_name eq 'Content') {
663 $s .= element_content_def ($src, $Info);
664 } elsif ($src->local_name eq 'IfModuleSet') {
665 $s .= qq(<![%@{[$src->get_attribute_value ('ModuleSet')]}.module;[\n);
666 $s .= submodule_declarations ($src, $Info);
667 $s .= qq(<!-- end of -->]]>\n);
668 } elsif ($src->local_name eq 'ElementSwitch') {
669 $s .= qq(<!ENTITY % @{[name_of ($src, $Info)]}.element "@{[$src->get_attribute_value ('Use')>0?'INCLUDE':'IGNORE']}">\n);
670 } elsif ($src->local_name eq 'AttributeSwitch') {
671 $s .= qq(<!ENTITY % @{[name_of ($src, $Info)]}.attlist "@{[$src->get_attribute_value ('Use')>0?'INCLUDE':'IGNORE']}">\n);
672 } elsif ($src->local_name eq 'ModuleSwitch') {
673 $s .= qq(<!ENTITY % @{[name_of ($src, $Info)]}.module "@{[$src->get_attribute_value ('Use')>0?'INCLUDE':'IGNORE']}">\n);
674 } elsif ($src->local_name eq 'GeneralEntity') {
675 $s .= entity_declaration ($src, $Info, param => 0);
676 } elsif ($src->local_name eq 'ParameterEntity') {
677 $s .= entity_declaration ($src, $Info, param => 1);
678 }
679 }
680 $s;
681 }
682
683 sub element_content_def ($$) {
684 my ($src, $Info) = @_;
685 qq(<!ENTITY % @{[name_of ($src, $Info, key => 'ElementType')]}.content @{[paralit convert_content_model ($src, $Info, default => 'EMPTY')]}>\n);
686 }
687
688 sub element_def ($$) {
689 my ($src, $Info) = @_;
690 my $name = get_name ($src, $Info);
691 my $mname = $name =~ /^\Q$Info->{ID}.\E/ ? $name : qq($Info->{ID}.$name);
692 my $short_name = $name;
693 if ($name =~ /^\Q$Info->{ID}\E\.(.+)/) {
694 $Info->{QName}->{$1} = 1;
695 push @{$Info->{elements}}, $1;
696 $short_name = $1;
697 }
698 my $s = get_desc $src, $Info, prefix => qq($short_name: ),
699 padding_length => 51, padding_dot => q(.);
700 $s .= "\n";
701 $s .= xml_parameter_ENTITY qq($mname.element), value => 'INCLUDE';
702 $s .= xml_condition_section (qq($mname.element) =>
703 xml_parameter_ENTITY
704 (qq($name.content),
705 value => convert_content_model ($src, $Info, default => 'EMPTY'))
706 . xml_parameter_ENTITY (qq($name.qname), value => $short_name)
707 . qq(<!ELEMENT %$name.qname; %$name.content;>\n));
708 $s .= "\n";
709 $s .= attlist_def (scalar $src->get_attribute ('Attribute', make_new_node => 1), $Info, $mname);
710 $s;
711 }
712
713 sub get_model_token ($$) {
714 my ($name, $Info) = @_;
715 my $suffix = '.qname';
716 if ($name =~ s/^\$//) {
717 $suffix = $name =~ /\.(?:mix|class|content|datatype)$/ ? '' : '.class';
718 }
719 if ($name =~ /^:(.+)/) {
720 $name = qq(%$1$suffix;);
721 } elsif ($name =~ /([^:]+):(.+)/) {
722 $name = qq(%$1.$2$suffix;);
723 } elsif ($name =~ /"([^"]+)"/) {
724 $name = qq($1);
725 } else {
726 $name = qq(%$Info->{ID}.$name$suffix;);
727 }
728 $name;
729 }
730
731 sub attlist_def ($$;$) {
732 my ($src, $Info, $name) = @_;
733 $name ||= get_name ($src, $Info, 'ElementType');
734 my $mname = get_name ($src, $Info);
735 $mname = ($name =~ /^\Q$Info->{ID}.\E/ ? $name : qq($Info->{ID}.$name))
736 if $mname eq "$Info->{ID}.";
737 $Info->{QName}->{$1} = 1 if $name =~ /^\Q$Info->{ID}\E\.(.+)/;
738 my $s = qq(<!ATTLIST %$name.qname;);
739 for my $src (@{$src->child_nodes}) {
740 ## Attribute Definition
741 if ($src->local_name eq 'Attribute') {
742 $s .= "\n\t". attrib_def ($src, $Info);
743 ## Reference to Attribute Definition
744 } elsif ($src->local_name eq 'ref') {
745 $s .= "\n\t". attrib_ref ($src, $Info);
746 } elsif ($src->local_name eq 'REF') {
747 $s .= "\n\t". attrib_REF ($src, $Info);
748 }
749 }
750 if ($_[2]) {
751 $s .= qq(\n\t%$Info->{ID}.common.attrib;);
752 }
753 $s .= qq(>\n);
754 qq(@{[description ($src, $Info)]}<!ENTITY % $mname.attlist "INCLUDE">\n)
755 . xml_condition_section (qq($mname.attlist) => $s)
756 . "\n";
757 }
758
759 sub make_module ($$$$;%) {
760 my ($src, $Info, $id, $s, %opt) = @_;
761 my $name = $src->get_attribute_value ('Name')
762 || {arch => q/Base Architecture/,
763 attribs => q/Common Attributes/,
764 blkphras => q/Block Phrasal/,
765 blkpres => q/Block Presentation/,
766 blkstruct => q/Block Structural/,
767 charent => q/Character Entities/,
768 datatype => q/Datatypes/,
769 framework => q/Modular Framework/,
770 inlphras => q/Inline Phrasal/,
771 inlpres => q/Inline Presentation/,
772 inlstruct => q/Inline Structural/,
773 legacy => q/Legacy Markup/,
774 list => q/Lists/,
775 meta => q/Metainformation/,
776 model => q/Document Model/,
777 notations => q/Notations/,
778 pres => q/Presentation/,
779 qname => q/QName (Qualified Name)/,
780 struct => q/Document Structure/,
781 text => q/Text/,
782 }->{$id}
783 || $id;
784 return unless $s;
785
786 my $r = <<EOH;
787 <!-- ...................................................................... -->
788 <!-- @{[do{
789 my $s = qq($Info->{Name} $name Module );
790 if (70 - length $s > 0) {
791 $s = dot_padding $s, length => 70, dot => q(.);
792 } else {
793 $s = qq( $name Module );
794 $s = qq($Info->{Name}\n ) . dot_padding $s, length => 70, dot => q(.);
795 }
796 $s;
797 }]} -->
798 <!-- file: $Info->{ID}-$id.mod
799
800 $Info->{Description}
801 Copyright @{[(gmtime)[5]+1900]} $Info->{Copyright}, All Rights Reserved.
802 Revision: @{[sprintf '%04d-%02d-%02dT%02d:%02d:%02d+00:00',
803 (gmtime)[5]+1900, (gmtime)[4]+1, (gmtime)[3,2,1,0]
804 ]} (Generated by $SCRIPT_NAME/$VERSION)
805
806 This DTD module is identified by the SYSTEM identifier:
807
808 SYSTEM "$Info->{BaseURI}$Info->{ID}-$id.mod"
809
810 ...................................................................... -->
811
812 EOH
813 ## TODO: Support PUBLIC identifier.
814
815 ## Module description
816 my @para = ({
817 arch => (join "\n",
818 q!This optional module includes declarations that enable to be used!,
819 q!as a base architecture according to the 'Architectural Forms Definition!,
820 q!Requirements' (Annex A.3, ISO/IEC 10744, 2nd edition). For more!,
821 q!information on use of architectural forms, see the HyTime web site at!,
822 q!<http://www.hytime.org/>.!),
823 attribs => q/This module declares many of the common attributes./,
824 blkphras => qq/This module declares the element types and their attributes used\n/.
825 q/to support block-level phrasal markup./,
826 blkpres => qq/This module declares the element types and their attributes used\n/.
827 q/to support block-level presentational markup./,
828 blkstruct => qq/This module declares the element types and their attributes used\n/.
829 q/to support block-level structural markup./,
830 charent => q/This module declares the set of character entities./,
831 datatype => q/This module defines containers for the datatypes./,
832 framework => qq/This module imstantiates the modules needed to support\n/.
833 q/the modularization model./,
834 inlphras => qq/This module declares the element types and their attributes used\n/.
835 q/to support inline phrasal markup./,
836 inlpres => qq/This module declares the element types and their attributes used\n/.
837 q/to support inline presentational markup./,
838 inlstruct => qq/This module declares the element types and their attributes used\n/.
839 q/to support inline structural markup./,
840 legacy => q/This module declares additional markup that is considered obsolete./,
841 list => qq/This module declares the list-oriented element types\n/.
842 q/and their attributes./,
843 meta => qq/This module declares the element types and their attributes\n/.
844 q/to support metainformation markup./,
845 model => qq/This model describes the groupings of element types that\n/.
846 q/make up common content models./,
847 pres => qq/This module declares the element types and their attributes used\n/.
848 q/to support presentational markup./,
849 qname => (join "\n",
850 q!This module is contained in two parts, labeled Section 'A' and 'B':!,
851 q!!,
852 q! Section A declares parameter entities to support namespace-qualified!,
853 q! names, namespace declarations, and name prefixing.!,
854 q!!,
855 q! Section B declares parameter entities used to provide namespace-qualified!,
856 q! names for all element types and global attribute names.!),
857 struct => qq/This module defines the major structural element types and\n/.
858 q/their attributes./,
859 }->{$id}, $src->get_attribute_value ('Description'));
860 unshift @para, ' '.join ', ', sort @{$Info->{elements}||[]} if @{$Info->{elements}||[]};
861 if (@para) {
862 $r .= <<EOH;
863 <!-- $name
864
865 @{[make_paragraphs \@para, indent => ' ']}
866 -->
867
868 EOH
869 }
870
871 $r .= $s;
872
873 $r .= qq(\n<!-- end of $Info->{ID}-$id.mod -->\n);
874
875 my $file = qq"$Info->{ID}-$id.mod";
876 open FILE, '>', $file or die "$0: $file: $!";
877 print FILE $r;
878 close FILE;
879 print STDERR "$0: $file created\n";
880 }
881
882 sub make_dtd ($$$$) {
883 my ($src, $Info, $id, $s) = @_;
884 $id = "-$id" if $id;
885
886 my $r = <<EOH;
887 <!-- ....................................................................... -->
888 <!-- @{[ dot_padding "$Info->{Name} DTD ", length => 71, dot => q(.) ]} -->
889 <!-- file: $Info->{ID}.dtd
890 -->
891
892 <!-- $Info->{Name} DTD
893
894 $Info->{Description}
895
896 Copyright @{[(gmtime)[5]+1900]} $Info->{Copyright}, All Rights Reserved.
897
898 Permission to use, copy, modify and distribute this DTD and its
899 accompanying documentation for any purpose and without fee is hereby
900 granted in perpetuity, provided that the above copyright notice and
901 this paragraph appear in all copies. The copyright holders make no
902 representation about the suitability of the DTD for any purpose.
903
904 It is provided "as is" without expressed or implied warranty.
905
906 Revision: @{[sprintf '%04d-%02d-%02dT%02d:%02d:%02d+00:00',
907 (gmtime)[5]+1900, (gmtime)[4]+1, (gmtime)[3,2,1,0]]}
908
909 -->
910 <!-- This is the driver file for the $Info->{Name} DTD.
911
912 This DTD is identified by the SYSTEM identifier:
913
914 SYSTEM "$Info->{BaseURI}$Info->{ID}$id.dtd"
915 -->
916
917 EOH
918
919 $r .= $s;
920
921 $r .= qq(\n<!-- end of $Info->{ID}$id.dtd -->\n);
922
923 my $file = qq"$Info->{ID}$id.dtd";
924 open FILE, '>', $file or die "$0: $file: $!";
925 print FILE $r;
926 close FILE;
927 print STDERR "$0: $file created\n";
928 }
929
930
931 =head1 NAME
932
933 mkdtds.pl - Modularized XML Document Type Definition (DTD) Generator
934
935 =head1 DESCRIPTION
936
937 This script generates XML DTD module implementations and/or DTD drivers,
938 that can be used with modularized XHTML DTDs.
939
940 =head1 USAGE
941
942 $ perl mkdtds.pl driver.dds
943 mkdtds.pl: driver.dtd created
944 mkdtds.pl: driver-model.mod created
945
946 $ perl mkdtds.pl moduleset.dms
947 mkdtds.pl: moduleset-datatype.mod created
948 mkdtds.pl: moduleset-attrib.mod created
949 mkdtds.pl: moduleset-module1.mod created
950
951 =head1 DTD SOURCE FORMAT
952
953 (((See examples on <http://suika.fam.cx/gate/cvs/markup/>)))
954
955 =head1 REQUIRED MODULES
956
957 This script uses C<Message::Markup::SuikaWikiConfig20::Node> and
958 C<Message::Markup::SuikaWikiConfig20::Parser>. Please retrive it from
959 <http://suika.fam.cx/gate/cvs/messaging/manakai/lib/Message/Markup/SuikaWikiConfig20/>
960 and put into your C<lib> directory.
961
962 =head1 AUTHOR
963
964 Wakaba <w@suika.fam.cx>
965
966 =head1 LICENSE
967
968 Copyright 2003-2004 Wakaba <w@suika.fam.cx>
969
970 This program is free software; you can redistribute it and/or
971 modify it under the same terms as Perl itself.
972
973 Note that author claims no copyright with regard to DTD modules/drivers generated
974 by this script. Author(s) of DTD modules/drivers should explicily state their
975 license terms in them and their documentation (if any).
976
977 =cut

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24