# -*- Perl -*-
package SGML::DTDParse::ContentModel;
use strict;
use vars qw($VERSION $CVS);
$VERSION = do { my @r=(q$Revision: 2.1 $=~/\d+/g); sprintf "%d."."%03d"x$#r,@r };
$CVS = '$Id: ContentModel.pm,v 2.1 2005/07/02 23:51:18 ehood Exp $ ';
use strict;
use Text::DelimMatch;
use SGML::DTDParse::Tokenizer;
require 5.000;
require Carp;
{
package SGML::DTDParse::ContentModel::Group;
sub new {
my($type, $tok) = @_;
my($class) = ref($type) || $type;
my($self) = {};
my(@toks);
my(@model);
local($_);
bless $self, $class;
# print "Group:\n";
# $tok->print();
# print "\n";
foreach $_ ('CONTENT_MODEL_STRING',
'OCCURRENCE') {
$self->{$_} = $tok->{$_};
}
$self->{'CONNECTOR'} = '';
@toks = @{$tok->{'CONTENT_MODEL'}->{'MODEL'}};
if ($toks[1]) { # if there is a connector...
if (ref $toks[1] eq 'SGML::DTDParse::Tokenizer::Connector') {
$self->{'CONNECTOR'} = $toks[1]->{'CONNECTOR'};
}
}
$self->{'CONTENT_MODEL'} = new SGML::DTDParse::ContentModel $tok->{'CONTENT_MODEL'};
return $self;
}
sub content_model {
my $self = shift;
return $self->{'CONTENT_MODEL'};
}
sub print {
my($self, $depth) = @_;
print "\t" x $depth, "(connector: ", $self->{'CONNECTOR'}, "\n";
$self->{'CONTENT_MODEL'}->print($depth+1);
print "\t" x $depth, ")\n";
}
sub xml {
my($self, $depth) = @_;
my($con) = $self->{'CONNECTOR'};
my($occ) = $self->{'OCCURRENCE'};
my($type) = "";
my($xml) = "";
$xml .= " " x $depth;
if ($con eq '|') {
$type = "or-group";
} elsif ($con eq '&') {
$type = 'and-group';
} else {
$type = 'sequence-group';
}
if ($occ) {
$xml .= "<$type occurrence=\"$occ\">\n";
} else {
$xml .= "<$type>\n";
}
$xml .= $self->{'CONTENT_MODEL'}->xml($depth+1,1);
$xml .= " " x $depth;
$xml .= "$type>\n";
return $xml;
}
}
{
package SGML::DTDParse::ContentModel::Element;
sub new {
my($type, $tok) = @_;
my($class) = ref($type) || $type;
my($self) = {};
my($model);
bless $self, $class;
foreach $_ ('ELEMENT',
'OCCURRENCE') {
$self->{$_} = $tok->{$_};
}
return $self;
}
sub element {
my $self = shift;
return $self->{'ELEMENT'};
}
sub print {
my($self, $depth) = @_;
print "\t" x $depth, $self->{'ELEMENT'}, $self->{'OCCURRENCE'}, "\n";
}
sub xml {
my($self, $depth) = @_;
my($occ) = $self->{'OCCURRENCE'};
my($xml) = "";
$xml .= " " x $depth;
if ($self->{'ELEMENT'} eq '#PCDATA') {
$xml .= "\n";
} elsif ($self->{'ELEMENT'} eq 'ANY') {
$xml .= "\n";
} elsif ($self->{'ELEMENT'} eq 'EMPTY') {
$xml .= "\n";
} elsif ($self->{'ELEMENT'} eq 'CDATA') {
$xml .= "\n";
} elsif ($self->{'ELEMENT'} eq 'RCDATA') {
$xml .= "\n";
} else {
$xml .= "{'ELEMENT'} . "\"";
$xml .= " occurrence=\"$occ\"" if $occ;
$xml .= "/>\n";
}
return $xml;
}
}
{
package SGML::DTDParse::ContentModel::ParameterEntity;
sub new {
my($type, $tok) = @_;
my($class) = ref($type) || $type;
my($self) = {};
my($model);
bless $self, $class;
$self->{'PARAMETER_ENTITY'} = $tok->{'PARAMETER_ENTITY'};
return $self;
}
sub print {
my($self, $depth) = @_;
print "\t" x $depth, "%", $self->{'PARAMETER_ENTITY'}, ";\n";
}
sub xml {
my($self, $depth) = @_;
my($xml) = "";
$xml .= " " x $depth;
$xml .= "{'PARAMETER_ENTITY'} . "\"";
$xml .= "/>\n";
return $xml;
}
}
sub new {
my($type, $model) = @_;
my $class = ref($type) || $type;
my $self = {};
my(@toks) = ();
my(@model) = ();
bless $self, $class;
$self->{'CONTENT_MODEL_STRING'} = $model->{'CONTENT_MODEL_STRING'};
@toks = @{$model->{'MODEL'}};
# Note: we know that the first token will always be a group, unless
# the content model is declard content. See new() in Tokenizer.
#
while (@toks) {
my($tok) = shift @toks;
if (ref $tok eq 'SGML::DTDParse::Tokenizer::Group') {
push (@model, new SGML::DTDParse::ContentModel::Group $tok);
} elsif (ref $tok eq 'SGML::DTDParse::Tokenizer::Element') {
push (@model, new SGML::DTDParse::ContentModel::Element $tok);
} elsif (ref $tok eq 'SGML::DTDParse::Tokenizer::ParameterEntity') {
push (@model, new SGML::DTDParse::ContentModel::ParameterEntity $tok);
} elsif (ref $tok eq 'SGML::DTDParse::Tokenizer::Connector') {
#nop;
} else {
die "Bad token in SGML::DTDParse::ContentModel";
}
}
@{$self->{'MODEL'}} = @model;
return $self;
}
sub type {
my $self = shift;
my $depth = shift;
my @model = @{$self->{'MODEL'}};
$depth = 0 if !defined($depth);
while (@model) {
my $tok = shift @model;
if ((ref $tok) =~ /Element$/) {
return 'mixed' if $tok->element() eq '#PCDATA';
if ($depth == 0) {
return 'cdata' if $tok->element() eq 'CDATA';
return 'rcdata' if $tok->element() eq 'RCDATA';
return 'empty' if $tok->element() eq 'RCDATA';
}
} elsif ((ref $tok) =~ /Group$/) {
my $cm = $tok->content_model();
return $cm->type($depth+1);
}
}
return 'element';
}
sub print {
my($self) = shift;
my($depth) = shift || 1;
my(@model) = @{$self->{'MODEL'}};
local($_);
foreach $_ (@model) {
$_->print($depth);
}
}
sub xml {
my($self) = shift;
my($depth) = shift || 1;
my($internal) = shift;
my(@model) = @{$self->{'MODEL'}};
my($xml) = "";
my($tag);
local($_);
if (!$internal) {
$tag = $depth;
$depth = 1;
# $xml .= "<$tag string=\"";
# $xml .= $self->{'CONTENT_MODEL_STRING'};
# $xml .= "\">\n";
}
foreach $_ (@model) {
$xml .= $_->xml($depth);
}
# if (!$internal) {
# $xml .= "$tag>\n";
# }
return $xml;
}
1;