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

Contents of /markup/tool/mkdtds.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (show annotations) (download)
Fri Oct 24 13:37:38 2003 UTC (20 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.1: +103 -7 lines
File MIME type: text/plain
Support of full-DTD importing

1 use strict;
2 {require SuikaWiki::Markup::SuikaWikiConfig20::Parser;
3
4 my $parser = new SuikaWiki::Markup::SuikaWikiConfig20::Parser;
5 local $/ = undef;
6 my $src = $parser->parse_text (scalar <>);
7 my $Info = {};
8
9 for my $src ($src->get_attribute ('ModuleSet')
10 || $src->get_attribute ('DocumentType')) {
11 for (qw/ID Copyright BaseURI/) {
12 $Info->{$_} = $src->get_attribute_value ($_);
13 }
14 $Info->{Name} = $src->get_attribute_value ('Name')
15 .' '.$src->get_attribute_value ('Version');
16 $Info->{ns} = $src->get_attribute ('Namespace');
17 }
18
19 for (@{$src->child_nodes}) {
20 if ($_->local_name eq 'Attribute') {
21 attrib_module ($_, $Info);
22 } elsif ($_->local_name eq 'Datatype') {
23 datatype_module ($_, $Info);
24 } elsif ($_->local_name eq 'Notation') {
25 notation_module ($_, $Info);
26 } elsif ($_->local_name eq 'Module') {
27 submodule ($_, $Info);
28 } elsif ($_->local_name eq 'Model') {
29 model_module ($_, $Info);
30 $Info->{has_model} = 1;
31 } elsif ($_->local_name eq 'Driver') {
32 dtd_driver ($_, $Info);
33 }
34 }
35
36 if (ref $src->get_attribute ('ModuleSet')) {
37 qname_module ($src->get_attribute ('ModuleSet'), $Info);
38 }
39 exit}
40
41 sub submodule_id_of ($$;%) {
42 my ($src, $Info, %opt) = @_;
43 my $id = $src->get_attribute_value ('ID') || $opt{default};
44 unless ($id) {
45 die "$0: Submodule identifier not specified";
46 }
47 $id;
48 }
49 sub xml_datatype_of ($$;%) {
50 my ($src, $Info, %opt) = @_;
51 my $type = $src->get_attribute_value ('XMLType') || $opt{default};
52 $type =~ s/\s+//g;
53 $type;
54 }
55 sub system_id_of ($$;%) {
56 my ($src, $Info, %opt) = @_;
57 my $sysid = $src->get_attribute_value ('SYSTEM');
58 if ($sysid =~ /<([^>]+)>/) {
59 return $1;
60 } else {
61 return $opt{base}.($sysid || $opt{default});
62 }
63 }
64 sub external_id_of ($$;%) {
65 my ($src, $Info, %opt) = @_;
66 my $sysid = system_id_of ($src, $Info, %opt);
67 my $pubid = $src->get_attribute_value ('PUBLIC');
68 if ($pubid) {
69 if ($sysid) {
70 return qq(PUBLIC "$pubid"\n\t "$sysid");
71 } else {
72 return qq(PUBLIC "$pubid");
73 }
74 } else {
75 return qq(SYSTEM "$sysid");
76 }
77 }
78 sub name_of ($$;%) {
79 my ($src, $Info, %opt) = @_;
80 unless (ref $src) {require Carp; Carp::croak ('$src undefined')}
81 my $name = $src->get_attribute_value ($opt{key} || 'Name');
82 if ($name =~ /^:(.+)/) { ## Global namespace
83 return $1;
84 } elsif ($name =~ /([^:]+):(.+)/) { ## Named space
85 return $1.($opt{delim}||'.').$2;
86 } else { ## Default namespace
87 return $Info->{ID}.($opt{delim}||'.').$name;
88 }
89 }
90 sub local_name_of ($$;%) {
91 my ($src, $Info, %opt) = @_;
92 my $name = $src->get_attribute_value ($opt{key} || 'Name');
93 if ($name =~ /^:(.+)/) { ## Global namespace
94 return $1;
95 } elsif ($name =~ /[^:]+:(.+)/) { ## Named space
96 return $1;
97 } else { ## Default namespace
98 return $name;
99 }
100 }
101 sub set_name_of ($$;%) {
102 my ($src, $Info, %opt) = @_;
103 my $name = $src->get_attribute_value ($opt{key} || 'Name');
104 if ($name =~ /^:.+/) { ## Global namespace
105 return 'XHTML';
106 } elsif ($name =~ /([^:]+):.+/) { ## Named space
107 return $1;
108 } else { ## Default namespace
109 return $Info->{ID};
110 }
111 }
112 sub class_name_of ($$;%) {
113 my ($src, $Info, %opt) = @_;
114 my $name = name_of ($src, $Info, %opt);
115 unless ($name =~ /\.(class|mix|content|datatype)$/) {
116 $name .= '.class';
117 }
118 $name;
119 }
120 sub convert_content_model ($$;%) {
121 my ($src, $Info, %opt) = @_;
122 my $model = $src->get_attribute_value ($opt{key} || 'Content') || $opt{default};
123 $model =~ s/\s//g;
124 my $nonsymbol = qr/[^%#?,\$;()+*|:]/;
125 $model =~ s/(?<![%#.])((?:\$|\b)$nonsymbol+(?::$nonsymbol+)?|\$?:$nonsymbol+|"[^"]+")/get_model_token ($1, $Info)/ge;
126 $model;
127 }
128 sub sparalit ($) {
129 my $s = paralit (shift);
130 $s =~ s/&/&#x26;/g;
131 $s =~ s/%/&#x25;/g;
132 $s;
133 }
134 sub paralit ($) {
135 my $s = shift;
136 if ($s =~ /"/) {
137 if ($s =~ /'/) {
138 $s =~ s/'/&#x27;/g;
139 return qq("$s");
140 } else {
141 return qq('$s');
142 }
143 } else {
144 return qq("$s");
145 }
146 }
147 sub description ($$;%) {
148 my ($src, $Info, %opt) = @_;
149 my $desc = $src->get_attribute_value ('Description');
150 $desc =~ s/\n/\n /g;
151 unless ($desc) {
152 $desc = {
153 load_module => {
154 AttributeModule => q/Common Attributes Module/,
155 DatatypeModule => q/Datatypes Module/,
156 NotationModule => q/Notation Module/,
157 QNameModule => q/QName Module/,
158 },
159 }->{$opt{context}}->{$opt{id} || $src->get_attribute_value ($opt{id_key}||'ID')};
160 }
161 $desc = qq(<!-- $desc -->\n) if $desc;
162 $desc;
163 }
164
165 sub entity_declaration ($$;%) {
166 my ($src, $Info, %opt) = @_;
167 my $val;
168 if ($src->get_attribute_value ('ID')
169 || $src->get_attribute_value ('SYSTEM')
170 || $src->get_attribute_value ('PUBLIC')) {
171 $val = "\n\t".external_id_of ($src, $Info, default => $src->get_attribute_value ('ID'));
172 } elsif (ref $src->get_attribute ('Declaration')) {
173 $val = "\n\t".sparalit submodule_declarations ($src->get_attribute ('Declaration'), $Info);
174 } else {
175 $val = paralit $src->get_attribute_value ('EntityValue');
176 }
177 my $s = <<EOH;
178 @{[description ($src, $Info)]}<!ENTITY @{[$opt{param}?'% ':'']}@{[$src->get_attribute_value ('Name')]} $val>
179
180 EOH
181 $s;
182 }
183
184 sub dtd_driver ($$) {
185 my ($src, $Info) = @_;
186 my $s = '';
187 my %s;
188 my @module_set;
189 for my $src (@{$src->child_nodes}) {
190 if ($src->local_name eq 'Module') {
191 $s .= dtd_driver_load_module ($src, $Info);
192 } elsif ($src->local_name eq 'DTD') {
193 $s .= dtd_driver_load_dtd ($src, $Info);
194 } elsif ($src->local_name eq 'ModuleSet') {
195 push @module_set, $src;
196 } elsif ($src->local_name =~ /^(?:QName|Attribute|Datatype|Notation)Module/) {
197 $s{$src->local_name} .= dtd_driver_load_module ($src, $Info);
198 } elsif ($src->local_name eq 'IfModuleSet') {
199 $s .= qq(<![%@{[$src->get_attribute_value ('ModuleSet')]}.module;[\n);
200 $s .= submodule_declarations ($src, $Info);
201 $s .= qq(]]>\n);
202 } elsif ($src->local_name eq 'GeneralEntity') {
203 $s .= entity_declaration ($src, $Info, param => 0);
204 } elsif ($src->local_name eq 'ParameterEntity') {
205 $s .= entity_declaration ($src, $Info, param => 1);
206 }
207 }
208
209 $s{ModelModule} = $src->get_attribute_value ('NoModelModule') ? '' :
210 $Info->{has_model} ? <<EOH : '';
211 <!-- Document Model module -->
212 <!ENTITY % $Info->{ID}-model.module "INCLUDE">
213 <![%$Info->{ID}-model.module;[
214 <!ENTITY % $Info->{ID}-model.decl
215 'SYSTEM "$Info->{ID}-model.mod"'>
216 <!ENTITY % $Info->{ID}-model.mod %$Info->{ID}-model.decl;>
217 %$Info->{ID}-model.mod;]]>
218
219 EOH
220
221 $s = dtd_driver_module_sets (\@module_set, $Info)
222 . $s{QNameModule}.$s{DatatypeModule}.$s{NotationModule}.$s{AttributeModule}
223 . $s{ModelModule}
224 .$s;
225 make_dtd ($src, $Info, $src->get_attribute_value ('ID'), $s);
226 }
227
228 sub dtd_driver_module_sets ($$) {
229 my ($srces, $Info) = @_;
230 my @src = map {{src => $_}} @$srces;
231 my $s = qq(<!-- Switchers to include/ignore each vocabulary -->\n);
232 for my $module_set (@src) {
233 $module_set->{ID} = $module_set->{src}->get_attribute_value ('ID') || 'XHTML';
234 $s .= qq(<!ENTITY % $module_set->{ID}.module "@{[$module_set->{src}->get_attribute_value ('Default') > 0 ? 'INCLUDE' : 'IGNORE']}">\n);
235 }
236 $s .= qq(\n<!-- Namespace names -->\n);
237 for my $module_set (@src) {
238 $module_set->{ns} = $module_set->{src}->get_attribute ('Namespace');
239 $s .= qq(<!ENTITY % $module_set->{ID}.xmlns "@{[$module_set->{ns}->get_attribute_value ('Name')]}">\n);
240 }
241 $s .= qq(\n<!-- Base URIs for the relavant DTD modules -->\n);
242 for my $module_set (@src) {
243 $s .= qq(<!ENTITY % $module_set->{ID}.sysid.base "@{[$module_set->{src}->get_attribute_value ('BaseURI')]}">\n);
244 }
245 $s .= qq(\n<!-- Namespace prefix -->\n);
246 $s .= qq(<!ENTITY % NS.prefixed "@{[$Info->{ns}->get_attribute_value ('UsePrefix') > 0 ? 'INCLUDE' : 'IGNORE']}">\n);
247 for my $module_set (@src) {
248 $s .= qq(<!ENTITY % $module_set->{ID}.prefix "@{[$module_set->{ns}->get_attribute_value ('DefaultPrefix')]}">\n);
249 $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);
250 }
251 $s .= qq(\n<!-- a URI reference -->\n<!ENTITY % URI.datatype "CDATA">\n);
252 $s .= qq(\n<!-- Placefolders for foreign namespace declarations -->\n);
253 for my $module_set (@src) {
254 $s .= qq(<!ENTITY % $module_set->{ID}.xmlns.extra.attrib "">\n);
255 }
256 $s .= qq(\n<!-- Namespace declarations -->\n);
257 for my $module_set (@src) {
258 $s .= qq(<![%$module_set->{ID}.module;[
259 <![%$module_set->{ID}.prefixed;[
260 <!ENTITY % $module_set->{ID}.xmlns.decl.attrib
261 "xmlns:%$module_set->{ID}.prefix; %URI.datatype; #FIXED '%$module_set->{ID}.xmlns;'">
262 ]]>
263 <!ENTITY % $module_set->{ID}.xmlns.decl.attrib
264 "xmlns %URI.datatype; #FIXED '%$module_set->{ID}.xmlns;'">
265 ]]>
266 <!ENTITY % $module_set->{ID}.xmlns.decl.attrib "">\n\n);
267 }
268 $s .= qq(\n<!ENTITY % NS.decl.attrib
269 ").join ("\n\t", (map {qq(%$_->{ID}.xmlns.decl.attrib;)} @src),
270 map {qq(%$_->{ID}.xmlns.extra.attrib;)} @src).qq(">\n);
271 $s .= qq(\n);
272 for my $module_set (@src) {
273 $s .= qq(<!ENTITY % $module_set->{ID}.xmlns.attrib "%NS.decl.attrib;">\n);
274 }
275 $s .= qq(\n\n);
276 $s;
277 }
278
279 sub dtd_driver_load_module ($$) {
280 my ($src, $Info) = @_;
281 my $module_name = name_of ($src, $Info, key => 'ID');
282 my $module_hyphen_name = name_of ($src, $Info, key => 'ID', delim => '-');
283 my $module_set_name = set_name_of ($src, $Info, key => 'ID');
284 my $module_id = local_name_of ($src, $Info, key => 'ID');
285
286 my $s .= <<EOH;
287 @{[description ($src, $Info, context => 'load_module', id => $src->local_name)]}<![%$module_set_name.module;[
288 <!ENTITY % $module_name.module "@{[$src->get_attribute_value ('Default') >= 0 ? 'INCLUDE' : 'IGNORE']}">
289 <![%$module_name.module;[
290 @{[submodule_declarations ($src, $Info)]}<!ENTITY % $module_name.decl
291 @{[paralit external_id_of ($src, $Info, default => qq($module_hyphen_name.mod), base => qq(%$module_set_name.sysid.base;))]}>
292 <!ENTITY % $module_name.mod %$module_name.decl;>
293 %$module_name.mod;]]>
294 ]]>
295
296 EOH
297 $s;
298 }
299
300 sub dtd_driver_load_dtd ($$) {
301 my ($src, $Info) = @_;
302 my $module_set_name = $src->get_attribute_value ('ID');
303
304 my $s .= <<EOH;
305 @{[description ($src, $Info)]}<![%$module_set_name.module;[
306 @{[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;))]}">
307 @{[do{
308 my $pubid = $src->get_attribute_value ('PUBLIC');
309 if ($pubid) {
310 qq(<!ENTITY % $module_set_name.dtd.fpi "$pubid">\n<!ENTITY % $module_set_name.dtd.fpi.defined "INCLUDE">\n);
311 } else {
312 qq(<!ENTITY % $module_set_name.dtd.fpi "">\n<!ENTITY % $module_set_name.dtd.fpi.defined "IGNORE">\n);
313 }
314 }]}
315 <![%$module_set_name.dtd.fpi.defined;[
316 <!ENTITY % $module_set_name.dtd.decl
317 'PUBLIC "%$module_set_name.dtd.fpi;"
318 "%$module_set_name.dtd.sysid;"'>
319 ]]>
320 <!ENTITY % $module_set_name.dtd.decl
321 'SYSTEM "%$module_set_name.dtd.sysid;"'>
322 <!ENTITY % $module_set_name.dtd %$module_set_name.dtd.decl;>
323 %$module_set_name.dtd;]]>
324
325 EOH
326 $s;
327 }
328
329 sub model_module ($$) {
330 my ($src, $Info) = @_;
331 my $s = '';
332 for my $src (@{$src->child_nodes}) {
333 if ($src->local_name eq 'Class') {
334 $s .= qq(@{[description ($src, $Info)]}<!ENTITY % @{[class_name_of ($src, $Info)]} @{[paralit convert_content_model ($src, $Info)]}>\n\n);
335 } elsif ($src->local_name eq 'Content') {
336 $s .= element_content_def ($src, $Info);
337 }
338 }
339 make_module ($src, $Info, submodule_id_of ($src, $Info, default => 'model'), $s);
340 }
341
342 sub datatype_module ($$) {
343 my ($src, $Info) = @_;
344 my $s = '';
345 for my $src (@{$src->child_nodes}) {
346 if ($src->local_name eq 'Type') {
347 $s .= qq(@{[get_desc ($src, $Info)]}<!ENTITY % @{[name_of ($src, $Info)]}.datatype "@{[xml_datatype_of ($src, $Info, default => 'CDATA')]}">\n\n);
348 }
349 }
350 make_module ($src, $Info, submodule_id_of ($src, $Info, default => 'datatype'), $s);
351 }
352
353 sub notation_module ($$) {
354 my ($src, $Info) = @_;
355 my $s = '';
356 for my $src (@{$src->child_nodes}) {
357 if ($src->local_name eq 'Notation') {
358 $s .= qq(@{[get_desc ($src, $Info)]}<!NOTATION @{[name_of ($src, $Info)]} @{[external_id_of ($src, $Info)]}>\n\n);
359 }
360 }
361 make_module ($src, $Info, submodule_id_of ($src, $Info, default => 'notation'), $s);
362 }
363
364 sub qname_module ($$) {
365 my ($src, $Info) = @_;
366 my $ID = $Info->{ID};
367 my $ns = $src->get_attribute ('Namespace');
368 my $s = <<EOH;
369 <!ENTITY % NS.prefixed "@{[$ns->get_attribute_value ('UsePrefix')==1?
370 q(INCLUDE):q(IGNORE)]}">
371
372 <!-- 1. Declare conditional section keyword, used to activate namespace prefixing. -->
373 <!ENTITY % $ID.prefixed "@{[$ns->get_attribute_value ('UsePrefix')==1?
374 q(INCLUDE):
375 $ns->get_attribute_value ('UsePrefix')==-1?
376 q(IGNORE):
377 q(%NS.prefixed;)]}">
378
379 <!-- 2. Declare a parameter entity containing the namespace name. -->
380 <!ENTITY % $ID.xmlns "@{[$ns->get_attribute_value ('Name')]}">
381
382 <!-- 3. Declare parameter entities containing the default namespace prefix
383 string to use when prefixing is enabled. -->
384 <!ENTITY % $ID.prefix "@{[$ns->get_attribute_value ('DefaultPrefix')]}">
385
386 <!-- 4. Declare parameter entities containing the colonized prefix
387 used when prefixing is active, an empty string when it is not. -->
388 <![%$ID.prefixed;[
389 <!ENTITY % $ID.pfx "%$ID.prefix;:">
390 ]]>
391 <!ENTITY % $ID.pfx "">
392
393 <!-- declare qualified name extensions here -->
394 <!ENTITY % ${ID}-qname-extra.mod "">
395 %${ID}-qname-extra.mod;
396
397 <!-- 5. May be redeclared to contain any foreign namespace declaration
398 attributes for namespaces embedded in XML. -->
399 <!ENTITY % $ID.xmlns.extra.attrib "">
400
401 <![%$ID.prefixed;[
402 <!ENTITY % $ID.xmlns.decl.attrib
403 "xmlns:%$ID.prefix; %URI.datatype; #FIXED '%$ID.xmlns;'">
404 ]]>
405 <!ENTITY % $ID.xmlns.decl.attrib
406 "xmlns %URI.datatype; #FIXED '%$ID.xmlns;'">
407
408 <![%$ID.prefixed;[
409 <!ENTITY % NS.decl.attrib
410 "%$ID.xmlns.decl.attrib;
411 %$ID.xmlns.extra.attrib;">
412 ]]>
413 <!ENTITY % NS.decl.attrib
414 "%$ID.xmlns.extra.attrib;">
415
416 <!-- Declare a parameter entity containing all XML namespace declaration
417 attributes used, including a default xmlns declaration when prefixing
418 is inactive. -->
419 <![%$ID.prefixed;[
420 <!ENTITY % $ID.xmlns.attrib
421 "%NS.decl.attrib;">
422 ]]>
423 <!ENTITY % $ID.xmlns.attrib
424 "%$ID.xmlns.decl.attrib;
425 %NS.decl.attrib;">
426
427 <!-- 6. Declare parameter entities used to provide namespace-qualified
428 names for all element types and global attribute names. -->
429 EOH
430 for my $lname (keys %{$Info->{QName}}) {
431 $s .= qq(<!ENTITY % $Info->{ID}.$lname.qname "%$Info->{ID}.pfx;$lname">\n);
432 }
433 $s .= qq(\n);
434 for my $lname (keys %{$Info->{QNameA}}) {
435 $s .= qq(<!ENTITY % $Info->{ID}.$lname.attrib.qname "%$Info->{ID}.prefix;:$lname">\n);
436 }
437 $s .= qq(\n);
438 for my $lname (keys %{$Info->{QNameB}}) {
439 $s .= qq(<!ENTITY % $Info->{ID}.$lname.attribute.qname "%$Info->{ID}.pfx;$lname">\n);
440 }
441 make_module ($src, $Info, 'qname', $s);
442 }
443
444 sub get_name ($$;$) {
445 my ($src, $Info, $key) = @_;
446 my $name = $src->get_attribute_value ($key || 'Name');
447 if ($name =~ /^:(.+)/) {
448 $name = $1;
449 } elsif ($name =~ /([^:]+):(.+)/) {
450 $name = qq($1.$2);
451 } else {
452 $name = qq($Info->{ID}.$name);
453 }
454 $name;
455 }
456
457 sub get_qname ($$) {
458 my ($src, $Info) = @_;
459 my $name = $src->get_attribute_value ('Name');
460 if ($name =~ /"([^"]+)"/) {
461 $name = qq($1);
462 } elsif ($name =~ /^:(.+)/) {
463 $name = qq(%$1.qname;);
464 } elsif ($name =~ /([^:]+):(.+)/) {
465 $name = qq(%$1.$2.qname;);
466 } elsif ($name =~ /\{([^{}]+)\}/) {
467 $Info->{QNameB}->{$1} = 1;
468 $name = qq(%$Info->{ID}.$1.attribute.qname;);
469 } else {
470 $Info->{QNameA}->{$name} = 1;
471 $name = qq(%$Info->{ID}.$name.attrib.qname;);
472 }
473 $name;
474 }
475
476 sub get_atype ($$) {
477 my ($src, $Info) = @_;
478 my $name = $src->get_attribute_value ('Type');
479 if ($name =~ /^:(.+)/) {
480 $name = qq(%$1.datatype;);
481 } elsif ($name =~ /([^:]+):(.+)/) {
482 $name = qq(%$1.$2.datatype;);
483 } elsif ($name =~ /"([^"]+)"/) {
484 $name = qq($1);
485 } else {
486 $name = qq(%$Info->{ID}.$name.datatype;);
487 }
488 $name;
489 }
490
491 sub get_adefault ($$) {
492 my ($src, $Info) = @_;
493 my $name = $src->get_attribute_value ('Default');
494 if (defined $name) {
495 } else {
496 $name = qq(#IMPLIED);
497 }
498 $name;
499 }
500
501 sub get_desc ($$) {
502 my ($src, $Info) = @_;
503 my $desc = $src->get_attribute_value ('Description');
504 $desc =~ s/\n/\n /g;
505 $desc = qq(<!-- $desc -->\n) if $desc;
506 $desc;
507 }
508
509 sub attset_def ($$) {
510 my ($src, $Info) = @_;
511 my $name = get_name ($src, $Info);
512 my $s .= qq(@{[get_desc ($src, $Info)]}<!ENTITY % $name.attrib\n\t);
513 my @s;
514 if ($name eq qq($Info->{ID}.common)) {
515 push @s, qq(%$Info->{ID}.common.extra.attrib;);
516 push @s, qq(%$Info->{ID}.xmlns.attrib;);
517 }
518 for my $src (@{$src->child_nodes}) {
519 ## Attribute Definition
520 if ($src->local_name eq 'Attribute') {
521 push @s, attrib_def ($src, $Info);
522 ## Reference to Attribute Definition
523 } elsif ($src->local_name eq 'ref') {
524 push @s, attrib_ref ($src, $Info);
525 } elsif ($src->local_name eq 'REF') {
526 push @s, attrib_REF ($src, $Info);
527 }
528 }
529 $s .= paralit join "\n\t", @s;
530 $s .= qq(>\n\n);
531 $s;
532 }
533
534 sub attrib_module ($$) {
535 my ($src, $Info) = @_;
536 my $s = <<EOH;
537 <!ENTITY % $Info->{ID}.common.extra.attrib "">
538
539 EOH
540 my $output_common = 0;
541 for my $src (@{$src->child_nodes}) {
542 ## Attributes Set
543 if ($src->local_name eq 'Attribute' or $src->local_name eq 'AttributeSet') {
544 $s .= attset_def ($src, $Info);
545 $output_common = 1 if get_name ($src, $Info) eq qq($Info->{ID}.common);
546 }
547 }
548 unless ($output_common) {
549 $s .= <<EOH;
550 <!ENTITY % $Info->{ID}.common.attrib
551 "%$Info->{ID}.common.extra.attrib;
552 %$Info->{ID}.xmlns.attrib;">
553
554 EOH
555 }
556 make_module ($src, $Info, ($src->get_attribute_value ('ID') || 'attribs'), $s);
557 }
558
559 sub attrib_def ($$) {
560 my ($src, $Info) = @_;
561 my $s = qq(@{[get_qname ($src, $Info)]} @{[get_atype ($src, $Info)]} @{[get_adefault ($src, $Info)]});
562 $s;
563 }
564
565 sub attrib_ref ($$) {
566 my ($src, $Info) = @_;
567 my $name = $src->value;
568 if ($name =~ /^:(.+)/) {
569 $name = $1;
570 } elsif ($name =~ /([^:]+):(.+)/) {
571 $name = qq($1.$2);
572 } else {
573 $name = qq($Info->{ID}.$name);
574 }
575 qq(%$name.attrib;);
576 }
577
578 sub attrib_REF ($$) {
579 my ($src, $Info) = @_;
580 {
581 'xml:base' => q<xml:base %URI.datatype; #IMPLIED>,
582 'xml:lang' => q<xml:lang %LanguageCode.datatype; #IMPLIED>,
583 'xml:space' => q<xml:space (default|preserve) #IMPLIED>,
584 }->{$src->value};
585 }
586
587 sub submodule ($$) {
588 my ($src, $Info) = @_;
589 my $s = submodule_declarations ($src, $Info);
590 make_module ($src, $Info, $src->get_attribute_value ('ID'), $s);
591 }
592
593 sub submodule_declarations ($$) {
594 my ($src, $Info) = @_;
595 my $s = '';
596 for my $src (@{$src->child_nodes}) {
597 if ($src->local_name eq 'Element') {
598 $s .= element_def ($src, $Info);
599 } elsif ($src->local_name eq 'Attribute') {
600 $s .= attlist_def ($src, $Info);
601 } elsif ($src->local_name eq 'AttributeSet') {
602 $s .= attset_def ($src, $Info);
603 } elsif ($src->local_name eq 'Class') {
604 $s .= qq(@{[description ($src, $Info)]}<!ENTITY % @{[class_name_of ($src, $Info)]} @{[paralit convert_content_model ($src, $Info)]}>\n\n);
605 } elsif ($src->local_name eq 'Content') {
606 $s .= element_content_def ($src, $Info);
607 } elsif ($src->local_name eq 'IfModuleSet') {
608 $s .= qq(<![%@{[$src->get_attribute_value ('ModuleSet')]}.module;[\n);
609 $s .= submodule_declarations ($src, $Info);
610 $s .= qq(]]>\n);
611 } elsif ($src->local_name eq 'ElementSwitch') {
612 $s .= qq(<!ENTITY % @{[name_of ($src, $Info)]}.element "@{[$src->get_attribute_value ('Use')>0?'INCLUDE':'IGNORE']}">\n);
613 } elsif ($src->local_name eq 'AttributeSwitch') {
614 $s .= qq(<!ENTITY % @{[name_of ($src, $Info)]}.attlist "@{[$src->get_attribute_value ('Use')>0?'INCLUDE':'IGNORE']}">\n);
615 } elsif ($src->local_name eq 'ModuleSwitch') {
616 $s .= qq(<!ENTITY % @{[name_of ($src, $Info)]}.module "@{[$src->get_attribute_value ('Use')>0?'INCLUDE':'IGNORE']}">\n);
617 } elsif ($src->local_name eq 'GeneralEntity') {
618 $s .= entity_declaration ($src, $Info, param => 0);
619 } elsif ($src->local_name eq 'ParameterEntity') {
620 $s .= entity_declaration ($src, $Info, param => 1);
621 }
622 }
623 $s;
624 }
625
626 sub element_content_def ($$) {
627 my ($src, $Info) = @_;
628 qq(<!ENTITY % @{[name_of ($src, $Info, key => 'ElementType')]}.content @{[paralit convert_content_model ($src, $Info, default => 'EMPTY')]}>\n);
629 }
630
631 sub element_def ($$) {
632 my ($src, $Info) = @_;
633 my $name = get_name ($src, $Info);
634 my $mname = $name =~ /^\Q$Info->{ID}.\E/ ? $name : qq($Info->{ID}.$name);
635 $Info->{QName}->{$1} = 1 if $name =~ /^\Q$Info->{ID}\E\.(.+)/;
636 my $s = <<EOH;
637 @{[get_desc ($src, $Info)]}<!ENTITY % $mname.element "INCLUDE">
638 <![%$mname.element;[
639 <!ENTITY % $name.content @{[paralit convert_content_model ($src, $Info, default => 'EMPTY')]}>
640 <!ELEMENT %$name.qname; %$name.content;>
641 ]]>
642 EOH
643 $s .= attlist_def (scalar $src->get_attribute ('Attribute', make_new_node => 1), $Info, $mname);
644 $s;
645 }
646
647 sub get_model_token ($$) {
648 my ($name, $Info) = @_;
649 my $suffix = '.qname';
650 if ($name =~ s/^\$//) {
651 $suffix = $name =~ /\.(?:mix|class|content|datatype)$/ ? '' : '.class';
652 }
653 if ($name =~ /^:(.+)/) {
654 $name = qq(%$1$suffix;);
655 } elsif ($name =~ /([^:]+):(.+)/) {
656 $name = qq(%$1.$2$suffix;);
657 } elsif ($name =~ /"([^"]+)"/) {
658 $name = qq($1);
659 } else {
660 $name = qq(%$Info->{ID}.$name$suffix;);
661 }
662 $name;
663 }
664
665 sub attlist_def ($$;$) {
666 my ($src, $Info, $name) = @_;
667 $name ||= get_name ($src, $Info, 'ElementType');
668 my $mname = get_name ($src, $Info);
669 $mname = ($name =~ /^\Q$Info->{ID}.\E/ ? $name : qq($Info->{ID}.$name))
670 if $mname eq "$Info->{ID}.";
671 $Info->{QName}->{$1} = 1 if $name =~ /^\Q$Info->{ID}\E\.(.+)/;
672 my $s = qq(@{[description ($src, $Info)]}<!ENTITY % $mname.attlist "INCLUDE">
673 <![%$mname.attlist;[
674 <!ATTLIST %$name.qname;);
675 for my $src (@{$src->child_nodes}) {
676 ## Attribute Definition
677 if ($src->local_name eq 'Attribute') {
678 $s .= "\n\t". attrib_def ($src, $Info);
679 ## Reference to Attribute Definition
680 } elsif ($src->local_name eq 'ref') {
681 $s .= "\n\t". attrib_ref ($src, $Info);
682 } elsif ($src->local_name eq 'REF') {
683 $s .= "\n\t". attrib_REF ($src, $Info);
684 }
685 }
686 if ($_[2]) {
687 $s .= qq(\n\t%$Info->{ID}.common.attrib;);
688 }
689 $s .= qq(>
690 ]]>
691
692 );
693 $s;
694 }
695
696 sub make_module ($$$$) {
697 my ($src, $Info, $id, $s) = @_;
698 my $name = $src->get_attribute_value ('Name')
699 || {attribs => q/Common Attributes/,
700 datatype => q/Datatypes/,
701 model => q/Document Model/,
702 qname => q/QName/,
703 struct => q/Structual/,
704 }->{$id}
705 || $id;
706 return unless $s;
707
708 my $r = <<EOH;
709 <!-- $Info->{Name} : $name Module
710
711 Copyright @{[(gmtime)[5]+1900]} $Info->{Copyright}
712 Revision: @{[sprintf '%04d-%02d-%02dT%02d:%02d:%02d+00:00',
713 (gmtime)[5]+1900, (gmtime)[4]+1, (gmtime)[3,2,1,0]]}
714
715 SYSTEM "$Info->{BaseURI}$Info->{ID}-$id.mod"
716 -->
717
718 EOH
719
720 $r .= $s;
721
722 $r .= qq(\n<!-- end of $Info->{ID}-$id.mod -->\n);
723
724 my $file = qq"$Info->{ID}-$id.mod";
725 open FILE, '>', $file or die "$0: $file: $!";
726 print FILE $r;
727 close FILE;
728 print STDERR "$0: $file created\n";
729 }
730
731 sub make_dtd ($$$$) {
732 my ($src, $Info, $id, $s) = @_;
733 $id = "-$id" if $id;
734
735 my $r = <<EOH;
736 <!-- $Info->{Name} : Document Type Definition
737
738 Copyright @{[(gmtime)[5]+1900]} $Info->{Copyright}
739 Revision: @{[sprintf '%04d-%02d-%02dT%02d:%02d:%02d+00:00',
740 (gmtime)[5]+1900, (gmtime)[4]+1, (gmtime)[3,2,1,0]]}
741
742 SYSTEM "$Info->{BaseURI}$Info->{ID}$id.dtd"
743 -->
744
745 EOH
746
747 $r .= $s;
748
749 $r .= qq(\n<!-- end of $Info->{ID}$id.dtd -->\n);
750
751 my $file = qq"$Info->{ID}$id.dtd";
752 open FILE, '>', $file or die "$0: $file: $!";
753 print FILE $r;
754 close FILE;
755 print STDERR "$0: $file created\n";
756 }
757
758
759 =head1 NAME
760
761 mkdtds.pl --- Moduralized XML Document Type Definition Generator
762
763 =head1 DESCRIPTION
764
765 This script can be used to generate XML DTD modules and driver
766 which is interoperable with XHTML DTD modules.
767
768 =head1 USAGE
769
770 $ perl mkdtds.pl driver.dds
771 mkdtds.pl: driver.dtd created
772 mkdtds.pl: driver-model.mod created
773
774 $ perl mkdtds.pl moduleset.dms
775 mkdtds.pl: moduleset-datatype.mod created
776 mkdtds.pl: moduleset-attrib.mod created
777 mkdtds.pl: moduleset-module1.mod created
778
779 =head1 DTD SOURCE FORMAT
780
781 (((See examples on <http://suika.fam.cx/gate/cvs/markup/>)))
782
783 =head1 REQUIRED MODULE
784
785 This script uses SuikaWiki::Markup::SuikaWikiConfig20 and
786 SuikaWiki::Markup::SuikaWikiConfig20::Parser.
787 Please get it from <http://suika.fam.cx/gate/cvs/suikawiki/script/lib/>
788 and put into your lib directory.
789
790 =head1 AUTHOR
791
792 Wakaba <w@suika.fam.cx>
793
794 =head1 LICENSE
795
796 Copyright 2003 Wakaba <w@suika.fam.cx>
797
798 This program is free software; you can redistribute it and/or
799 modify it under the same terms as Perl itself.
800
801 Note that author claims no right about DTD modules generated by this script.
802 Author(s) of DTD modules should be explicily state their license terms.
803
804 =cut

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24