# dtdformat module for RefEntrys # $Id: refentry.pl,v 2.1 2005/07/02 23:51:18 ehood Exp $ use SGML::DTDParse::Util qw(entify); $fileext = ".xml"; $config{'expanded-element-index'} = "elements"; $config{'unexpanded-element-index'} = "dtdelem"; $config{'expanded-entity-index'} = "entities"; $config{'unexpanded-entity-index'} = "dtdent"; $config{'notation-index'} = 'notations'; # ====================================================================== my $dtdparseHomepage = "http://sourceforge.net/projects/dtdparse/"; # ====================================================================== sub elementRefpurpose { my $count = shift; my $name = $elements[$count]; return "&$baseid.purp.elem.$name;"; } sub entityRefpurpose { my $count = shift; my $name = $entities[$count]; my $entity = $entities{$name}; return "&$baseid.purp." . $entity->getAttribute('type') . ".$name;"; } sub notationRefpurpose { my $count = shift; my $name = $notations[$count]; return "&$baseid.purp.notn.$name;"; } sub elementDescription { my $count = shift; return "desc\n"; } sub entityDescription { my $count = shift; return "desc\n"; } sub notationDescription { my $count = shift; return "desc\n"; } # ====================================================================== sub basenames { my @names = @_; my %basename = (); my %usedname = (); foreach my $name (@names) { my $count = 2; my $bname = lc($name); if ($usedname{$bname}) { $bname = lc($name) . $count; while ($usedname{$bname}) { $bname++; } } $basename{$name} = $bname; $usedname{$name} = 1; } return %basename; } # ====================================================================== sub formatElement { my $count = shift; my $html = ""; my $name = $elements[$count]; my $element = $elements{$name}; my $cmex = undef; my $cmunx = undef; my $incl = undef; my $excl = undef; my $node = $element->getFirstChild(); while ($node) { if ($node->getNodeType() == XML::DOM::ELEMENT_NODE) { $cmex = $node if $node->getTagName() eq 'content-model-expanded'; $cmunx = $node if $node->getTagName() eq 'content-model'; $incl = $node if $node->getTagName() eq 'inclusions'; $excl = $node if $node->getTagName() eq 'exclusions'; } $node = $node->getNextSibling(); } $html .= &formatElementHeader($count); $html .= &formatElementTitle($count); if ($option{'synopsis'}) { if ($expanded eq 'expanded') { $html .= &formatElementSynopsis($count, $cmex, $cmex); } else { $html .= &formatElementSynopsis($count, $cmunx, $cmex); } } $html .= &formatElementDescription($count, $count) if $option{'description'}; $html .= &formatElementExamples($count, $count) if $option{'examples'}; $html .= &formatElementFooter($count); } sub formatElementHeader { my $count = shift; my $html = ""; my $name = $elements[$count]; my $element = $elements{$name}; $html .= "\n"; $html .= "\n"; $html .= "\n\n"; return $html; } sub formatElementTitle { my $count = shift; my $name = $elements[$count]; my $element = $elements{$name}; my $html = ""; $html .= "\n"; $html .= ""; $html .= $element->getAttribute('name'); $html .= "\n"; $html .= "Element\n"; $html .= "\n\n"; $html .= "\n"; $html .= "" . $element->getAttribute('name') . "\n"; $html .= ""; $html .= &elementRefpurpose($count); $html .= "\n"; $html .= "\n\n"; } sub formatElementSynopsis { my $count = shift; my $name = $elements[$count]; my $element = $elements{$name}; my $cm = shift; my $cmex = shift; my $html = ""; # What are the possibilities: mixed content, element content, or # declared content... my $mixed = $element->getAttribute('content-type') eq 'mixed'; my $declared = (!$mixed && $element->getAttribute('content-type') ne 'element'); $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "getAttribute('name') . " ::=\n"; $html .= &formatContentModel($count, $cm); $html .= "\n"; $html .= "\n"; $html .= &formatInclusions($count, $incl) if $incl && $option{'inclusions'}; $html .= &formatExclusions($count, $excl) if $excl && $option{'exclusions'}; $html .= &formatAttributeList($count) if $option{'attributes'}; $html .= &formatTagMinimization($count) if $option{'tag-minimization'}; $html .= &formatElementAppearsIn($count) if $option{'appears-in'}; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n\n"; return $html; } sub formatInclusions { my $count = shift; my $cm = shift; my $name = $elements[$count]; my $element = $elements{$name}; my $html = ""; $html .= "\n"; $html .= "getElementsByTagName("attribute"); for (my $count = 0; $count < $attrs->getLength(); $count++) { my $attr = $attrs->item($count); my $name = $attr->getAttribute('name'); my $type = $attr->getAttribute('value'); my $decltype = $attr->getAttribute('type'); my $default = ""; if ($decltype eq '#IMPLIED') { $default = "None"; } elsif ($decltype eq '#REQUIRED') { $default = "Required"; } elsif ($decltype eq '#CONREF') { $default = "Content reference"; } else { $default = $attr->getAttribute('default'); if ($default =~ /\"/) { $default = "'" . $default . "'"; } else { $default = "\"" . $default . "\""; } } if ($decltype eq '#FIXED') { $default = $default . " (fixed)"; } $html .= "\n"; $html .= &formatCell($name); $html .= &formatValues($type, $attr); $html .= &formatCell($default); $html .= "\n"; } return $html; } sub formatCell { my $value = shift; return "$value\n"; } sub formatValues { my $values = shift; my $attr = shift; my $enum = $attr->getAttribute('enumeration'); my $html = ""; if ($enum eq 'no' || $enum eq '') { return &formatCell($values); } $html .= ""; if ($enum eq 'notation') { $html .= "Enumerated notation:\n"; } else { $html .= "Enumeration:\n"; } $html .= "\n"; foreach my $val (sort { uc($a) cmp uc($b) } split(/\s+/, $attr->getAttribute('value'))) { $html .= "$val\n"; } $html .= "\n"; return $html; } sub formatTagMinimization { my $count = shift; my $name = $elements[$count]; my $element = $elements{$name}; my $html = ""; my $stagm = $element->getAttribute('stagm') || "-"; my $etagm = $element->getAttribute('etagm') || "-"; if ($element->getAttribute('stagm') || $element->getAttribute('etagm')) { my (%min) = ('--' => "Both the start- and end-tags are required for this element.", 'OO' => "Both the start- and end-tags are optional for this element, if your SGML declaration allows tag minimization.", 'O-' => "The start-tag is optional for this element, if your SGML declaration allows tag minimization. The end-tag is required.", '-O' => "The start-tag is required for this element. The end-tag is optional, if your SGML declaration allows minimization." ); $html .= "\n"; $html .= "getAttribute('name'); $html .= "\n"; } else { $html .= "\n"; } } $html .= "\n"; } } return $html; } sub formatElementDescription { my $count = shift; my $name = $elements[$count]; my $element = $elements{$name}; my $desc = &elementDescription($count); my $html = ""; return "" if !defined($desc); $html .= "Description\n"; $html .= $desc; $html .= "\n\n"; $html .= &formatParents($count) if $option{'parents'}; $html .= &formatChildren($count) if $option{'children'}; $html .= "\n\n"; return $html; } sub formatParents { my $count = shift; my $name = $elements[$count]; my $element = $elements{$name}; my $html = ""; if (exists $PARENTS{$name}) { $html .= "Parents\n"; $html .= "These elements contain "; $html .= $element->getAttribute('name') . ":\n"; $html .= ""; my $pname; foreach $pname (sort { uc($a) cmp uc($b) } keys %{$PARENTS{$name}}) { my $child = $elements{$pname}; $html .= ""; $html .= ""; $html .= "" . $child->getAttribute('name') . ""; $html .= ""; $html .= "\n"; } $html .= ".\n"; $html .= "\n\n"; } return $html; } sub formatChildren { my $count = shift; my $name = $elements[$count]; my $element = $elements{$name}; my $html = ""; my $mixed = $element->getAttribute('content-type') eq 'mixed'; my $declared = (!$mixed && $element->getAttribute('content-type') ne 'element'); return "" if $declared; # can't be any children... if (exists $CHILDREN{$name} || exists $POSSINCL{$name} || exists $POSSEXCL{$name}) { $html .= "Children\n"; } if (exists $CHILDREN{$name}) { $html .= "The following elements occur in "; $html .= $element->getAttribute('name') . ":\n"; $html .= ""; my $cname; foreach $cname (sort { uc($a) cmp uc($b) } keys %{$CHILDREN{$name}}) { my $child = $elements{$cname}; die "Unexpected error (1): can't find element \"$cname\".\n" if !$child; $html .= ""; $html .= ""; $html .= ""; $html .= $child->getAttribute('name'); $html .= ""; $html .= ""; $html .= "\n"; } $html .= ".\n"; } if (exists $POSSINCL{$name}) { $html .= "In some contexts, the following elements are\n"; $html .= "allowed anywhere:\n"; $html .= "\n"; my $cname; foreach $cname (sort { uc($a) cmp uc($b) } keys %{$POSSINCL{$name}}) { my $child = $elements{$cname}; die "Unexpected error (2): can't find element \"$cname\".\n" if !$child; $html .= ""; $html .= ""; $html .= ""; $html .= $child->getAttribute('name'); $html .= ""; $html .= ""; $html .= "\n"; } $html .= ".\n\n"; } if (exists $POSSEXCL{$name}) { $html .= "In some contexts, the following elements are\n"; $html .= "excluded:\n"; $html .= "\n"; my $cname; foreach $cname (sort { uc($a) cmp uc($b) } keys %{$POSSEXCL{$name}}) { my $element = $elements{$cname}; $html .= ""; $html .= ""; $html .= ""; $html .= $element->getAttribute('name'); $html .= ""; $html .= ""; $html .= "\n"; } $html .= ".\n\n"; } if (exists $CHILDREN{$name} || exists $POSSINCL{$name} || exists $POSSEXCL{$name}) { $html .= "\n\n"; } return $html; } sub formatElementExamples { my $count = shift; my $name = $elements[$count]; my $element = $elements{$name}; return ""; } sub formatElementFooter { my $count = shift; my $html = ""; $html .= "\n"; return $html; } # ---------------------------------------------------------------------- my $state = 'NONE'; my $depth = 0; my $col = 0; sub formatContentModel { my $count = shift; my $cm = shift; my $node = $cm->getFirstChild(); my $html = ""; $state = "NONE"; $depth = 0; $col = 0; while ($node) { if ($node->getNodeType == XML::DOM::ELEMENT_NODE) { $html .= formatContentModelElement($node); } $node = $node->getNextSibling(); } return $html; } sub formatContentModelElement { my $node = shift; my $html = ""; if ($node->getNodeType == XML::DOM::ELEMENT_NODE) { if ($node->getTagName() eq 'sequence-group') { $html .= &formatCMGroup($node, ","); } elsif ($node->getTagName() eq 'or-group') { $html .= &formatCMGroup($node, "|"); } elsif ($node->getTagName() eq 'and-group') { $html .= &formatCMGroup($node, "&"); } elsif ($node->getTagName() eq 'element-name') { $html .= &formatCMElement($node); } elsif ($node->getTagName() eq 'parament-name') { $html .= &formatCMParament($node); } elsif ($node->getTagName() eq 'pcdata') { $html .= &formatCMPCDATA($node); } elsif ($node->getTagName() eq 'cdata') { $html .= &formatCMCDATA($node); } elsif ($node->getTagName() eq 'rcdata') { $html .= &formatCMRCDATA($node); } elsif ($node->getTagName() eq 'empty') { $html .= &formatCMEMPTY($node); } elsif ($node->getTagName() eq 'any') { $html .= &formatCMANY($node); } else { die "Unexpected node: \"" . $node->getTagName() . "\"\n"; } $node = $node->getNextSibling(); } else { die "Unexpected node type.\n"; } return $html; } sub formatCMGroup { my $group = shift; my $occur = $group->getAttribute('occurrence'); my $sep = shift; my $first = 1; my $html = ""; if ($state ne 'NONE' && $state ne 'OPEN') { $html .= "\n"; $html .= " " x $depth if $depth > 0; $col = $depth; $state = 'NEWLINE'; } $html .= "("; $state = 'OPEN'; $depth++; $col++; my $node = $group->getFirstChild(); while ($node) { if ($node->getNodeType == XML::DOM::ELEMENT_NODE) { if (!$first) { $html .= $sep; $col++; if ($state ne 'NEWLINE' && ($col > 60)) { $html .= "\n"; $html .= " " x $depth if $depth > 0; $col = $depth; $state = 'NEWLINE'; } } $html .= &formatContentModelElement($node); $first = 0; } $node = $node->getNextSibling(); } $html .= ")"; $col++; if ($occur) { $html .= $occur; $col++; } $state = 'CLOSE'; $depth--; return $html; } sub formatCMElement { my $element = shift; my $name = $element->getAttribute('name'); my $occur = $element->getAttribute('occurrence'); my $href = ""; my $html = ""; $name = lc($name) if !$option{'case-sensitive'}; if ($state eq 'CLOSE') { $html .= "\n"; $html .= " " x $depth if $depth > 0; $col = $depth; $state = 'NEWLINE'; } $html .= ""; $html .= $element->getAttribute('name'); $html .= ""; $col += length($name); if ($occur) { $html .= $occur; $col++; } $state = 'ELEMENT'; return $html; } sub formatCMParament { my $element = shift; my $name = $element->getAttribute('name'); my $html = ""; if ($state eq 'CLOSE') { $html .= "\n"; $html .= " " x $depth if $depth > 0; $col = $depth; $state = 'NEWLINE'; } $html .= ""; $html .= "\%" . $name . ";"; $html .= ""; $col += length($name) + 2; $state = 'PARAMENT'; return $html; } sub formatCMPCDATA { my $html = ""; $html .= "#PCDATA"; $col += 7; $state = 'PCDATA'; return $html; } sub formatCMCDATA { my $html = ""; $html .= "CDATA"; $col += 5; $state = 'CDATA'; return $html; } sub formatCMRCDATA { my $html = ""; $html .= "RCDATA"; $col += 5; $state = 'RCDATA'; return $html; } sub formatCMEMPTY { my $html = ""; $html .= "EMPTY"; $col += 5; $state = 'EMPTY'; return $html; } sub formatCMANY { my $html = ""; $html .= "ANY"; $col += 3; $state = 'ANY'; return $html; } # ====================================================================== sub formatEntity { my $count = shift; my $name = $entities[$count]; my $entity = $entities{$name}; my $html = ""; my $textnl; if ($expanded eq 'expanded') { $textnl = $entity->getElementsByTagName("text-expanded"); } else { $textnl = $entity->getElementsByTagName("text"); } $html .= &formatEntityHeader($count); $html .= &formatEntityTitle($count); $html .= &formatEntitySynopsis($count, $textnl) if $option{'synopsis'}; $html .= &formatEntityAppearsIn($count) if $option{'appears-in'}; $html .= &formatEntityDescription($count) if $option{'description'}; $html .= &formatEntityExamples($count) if $option{'examples'}; $html .= &formatEntityFooter($count); return $html; } sub formatEntityHeader { my $count = shift; my $html = ""; my $name = $entities[$count]; $html .= "\n"; $html .= "\n"; $html .= "\n\n"; return $html; } sub formatEntityTitle { my $count = shift; my $name = $entities[$count]; my $entity = $entities{$name}; my $type = $entity->getAttribute("type"); my $html = ""; $html .= "\n"; $html .= ""; $html .= $entity->getAttribute('name'); $html .= "\n"; if ($type eq 'gen') { $html .= "General Entity\n"; } elsif ($type eq 'ndata' || $type eq 'cdata' || $type eq 'sdata' || $type eq 'pi') { $html .= "" . uc($type) . " Entity\n"; } else { $html .= "Parameter Entity\n"; } $html .= "\n\n"; $html .= "\n"; $html .= "" . $entity->getAttribute('name') . "\n"; $html .= ""; $html .= &entityRefpurpose($count); $html .= "\n"; $html .= "\n\n"; } sub formatEntitySynopsis { my $count = shift; my $textnl = shift; my $name = $entities[$count]; my $entity = $entities{$name}; my $html = ""; my $type = $entity->getAttribute("type"); my $public = entify($entity->getAttribute("public")); my $system = entify($entity->getAttribute("system")); my $text = ""; if ($textnl->getLength() > 0) { my $textnode = $textnl->item(0); my $content = $textnode->getFirstChild(); if ($content) { $text = $content->getData(); } else { $text = ""; } } $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "$match"; } else { $html .= $match; } } else { $html .= $match; } } $html .= $text; $html .= "\n"; $html .= "\n"; } } if ($type eq 'ndata' || $type eq 'cdata') { my $notation = $entity->getAttribute('notation'); $html .= uc($type) . " Entity"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "getAttribute('name') . ":\n"; $html .= "\n"; for (my $count = 0; $count <= $#ents; $count++) { my $entity = $entities{$ents[$count]}; $html .= ""; $html .= "getAttribute('type') . "."; $html .= $entity->getAttribute('name') . "\">"; $html .= $entity->getAttribute('name'); $html .= ""; $html .= "\n"; } $html .= "\n"; $html .= "\n"; } return $html; } sub formatEntityDescription { my $count = shift; my $name = $entities[$count]; my $entity = $entities{$name}; my $desc = &entityDescription($count); my $html = ""; return "" if !defined($desc); $html .= "Description\n"; $html .= $desc; $html .= "\n\n"; return $html; } sub formatEntityExamples { my $count = shift; my $name = $entities[$count]; my $entity = $entities{$name}; return ""; } sub formatEntityFooter { my $count = shift; my $html = ""; $html .= "\n"; return $html; } # ====================================================================== sub formatNotation { my $count = shift; my $html = ""; my $name = $notations[$count]; my $element = $notations{$name}; $html .= &formatNotationHeader($count); $html .= &formatNotationTitle($count); if ($option{'synopsis'}) { $html .= &formatNotationSynopsis($count); } $html .= &formatNotationDescription($count) if $option{'description'}; $html .= &formatNotationExamples($count) if $option{'examples'}; $html .= &formatNotationFooter($count); } sub formatNotationHeader { my $count = shift; my $html = ""; my $name = $notations[$count]; $html .= "\n"; $html .= "\n"; $html .= "\n\n"; return $html; } sub formatNotationTitle { my $count = shift; my $name = $notations[$count]; my $notation = $notations{$name}; my $html = ""; $html .= "\n"; $html .= ""; $html .= $notation->getAttribute('name'); $html .= "\n"; $html .= "Notation\n"; $html .= "\n\n"; $html .= "\n"; $html .= "" . $notation->getAttribute('name') . "\n"; $html .= ""; $html .= ¬ationRefpurpose($count); $html .= "\n"; $html .= "\n\n"; } sub formatNotationSynopsis { my $count = shift; my $name = $notations[$count]; my $notation = $notations{$name}; my $html = ""; my $public = entify($notation->getAttribute("public")); my $system = entify($notation->getAttribute("system")); $html .= "\n"; if ($public) { $html .= "\nPublic identifier:\n"; $html .= "$public."; $html .= "\n\n"; } if ($system) { $html .= "\nSystem identifier:\n"; $html .= "$system."; $html .= "\n\n"; } if (!$public && !$system) { $html .= "\nSystem specified\n"; $html .= "without a system identifier."; $html .= "\n\n"; } return $html; } sub formatNotationDescription { my $count = shift; my $name = $notations[$count]; my $notation = $notations{$name}; my $desc = ¬ationDescription($count); my $html = ""; return "" if !defined($desc); $html .= "Description\n"; $html .= $desc; $html .= "\n\n"; return $html; } sub formatNotationExamples { my $count = shift; my $name = $notations[$count]; my $element = $notations{$name}; return ""; } sub formatNotationFooter { my $count = shift; my $html = ""; $html .= "\n"; return $html; } # ====================================================================== sub writeElementIndexes { my $basedir = shift; my $title = entify($dtd->getDocumentElement->getAttribute('title')); my ($entfile, $sgmfile, $sysdir); local (*F, $_); $entfile = $basedir . "/" . $config{$expanded . "-element-index"} . ".ent"; $sgmfile = $basedir . "/" . $config{$expanded . "-element-index"} . $fileext; $sysdir = $config{$expanded . "-element-dir"}; open (F, ">$entfile"); foreach $name (@elements) { my $basename = $ELEMBASE{$name}; print F "\n"; print F "\n"; } close (F); open (F, ">$sgmfile"); print F "$title Element Reference\n"; foreach $name (@elements) { print F "&$baseid.elem.$name;\n"; } print F "\n"; close (F); } sub writeEntityIndexes { my $basedir = shift; my $title = entify($dtd->getDocumentElement->getAttribute('title')); my ($entfile, $sgmfile, $sysdir); local (*F, $_); $entfile = $basedir . "/" . $config{$expanded . "-entity-index"} . ".ent"; $sgmfile = $basedir . "/" . $config{$expanded . "-entity-index"} . $fileext; $sysdir = $config{$expanded . "-entity-dir"}; open (F, ">$entfile"); foreach $name (@entities) { my $entity = $entities{$name}; my $basename = $ENTBASE{$name}; print F "\n"; print F "getAttribute('type'), ".$name \"purpose\">\n"; } close (F); open (F, ">$sgmfile"); print F "$title Entity Reference\n"; foreach $name (@entities) { print F "&$baseid.param.$name;\n"; } print F "\n"; close (F); } sub writeNotationIndexes { my $basedir = shift; my $title = entify($dtd->getDocumentElement->getAttribute('title')); my ($notnfile, $sgmfile, $sysdir); local (*F, $_); $notnfile = $basedir . "/" . $config{"notation-index"} . ".ent"; $sgmfile = $basedir . "/" . $config{"notation-index"} . $fileext; $sysdir = $config{"notation-dir"}; open (F, ">$notnfile"); foreach $name (@notations) { my $notation = $notations{$name}; my $basename = $NOTBASE{$name}; print F "\n"; print F "\n"; } close (F); open (F, ">$sgmfile"); print F "$title Notation Reference\n"; foreach $name (@notations) { print F "&$baseid.notn.$name;\n"; } print F "\n"; close (F); } sub writeIndex { my $basedir = shift; my $title = entify($dtd->getDocumentElement->getAttribute('title')); my $entfile = $config{"expanded-entity-index"}; my $elemfile = $config{"expanded-element-index"}; my $notfile = $config{"notation-index"}; my $root = $dtd->getDocumentElement(); my $elements = $root->getElementsByTagName('element'); my $entities = $root->getElementsByTagName('entity'); my $notations = $root->getElementsByTagName('notation'); my $elemcount = $elements->getLength(); my $entcount = $entities->getLength(); my $notcount = $notations->getLength(); local (*F, $_); # nop; } 1;