package HNS::Hnf::TreeBuilder; # $Id: TreeBuilder.pm,v 1.34 2000/07/20 03:55:23 kenji Exp $ ################################################################ =head1 NAME HNS::Hnf::TreeBuilder - 木構造構築クラス =head1 DESCRIPTION parse the hnf, and construct tree-structure =cut ################################################################ use strict qw(vars); use Image::Size; # CAT image use HNS::Hnf::Command; use HNS::Hnf::Warning; use HNS::Template; use HNS::CategoryList; use HNS::System; BEGIN { $HNS::Hnf::TreeBuilder::debug = 0; print "content-type: text/html\r\n\r\n" if ($HNS::Hnf::TreeBuilder::debug); } #@HNS::Hnf::TreeBuilder::ISA = qw(HNS::Hnf::Command::HNF); ################################################################ sub new ($) { my $class = shift; my $self = { top=>new HNS::Hnf::Command::HNF('HNF') # top element of parse tree }; bless $self, $class; } ################################################################ =head2 $t->Parse($line); $line を解析し、ノードを適切な場所に追加する。 =cut sub Parse($$) { my ($self, $line) = @_; # comment if ($line =~ /^!\#\s/){ return; } elsif ($line =~ s/^!\s//){ chomp($line); $line = "\n"; } # translate ~ to
$line =~ s/~$/
/; if ($line =~ m!^(/?[A-Z]+)\s(.*)$!){ # may be hnf-command my $cmd_name = $1; my $is_end = $cmd_name =~ s!^/!!; my @vars = ($cmd_name, split(/ /, $2)); if (!$is_end){ $self->start(@vars); # start command. ex) UL } else { $self->end(@vars); # end command. ex) /UL } } else { # plain text $self->text($line); } } =head2 $t->start(@vars); private: if start command apeared, first create new command object($e), second insert the object to valid position of the tree($self->{pos}). (*) checking command validation and placement. insert end tag when the command could be omit end-command. =cut sub start($@) { my ($self, @vars) = @_; my $cmd_name = $vars[0]; my $e; my $pos = $self->{top}->{pos} || $self->{top}; my $class = "HNS::Hnf::Command::$cmd_name"; eval("\$e = new $class(qq($cmd_name));"); if ($@){ # invalid command $pos->PushContent(join(' ', @vars)); $pos->PushContent("\n"); $pos->PushContent(HNS::Hnf::Warning::Message('Reserved', $cmd_name)) unless (($HNS::System::IgnoreReserved eq 'ON') || ($pos->{name} eq "PRE")); # don't warn if in PRE print "pos->{name}: $pos->{name}\n" if ($HNS::Hnf::TreeBuilder::debug); return; } # set attributes and element to new Command { my $n_attr = "${class}::NumAttr"; # if line is "LINK url explain of anchor", and $n_attr == 1, # $e->{attr} = ['LINK', 'url'] # $e->{arg_content} = 'explain of ahchor'; $n_attr = $$n_attr; $e->{attr} = [@vars[0..$n_attr]]; $e->{arg_content} = join(' ', @vars[$n_attr+1..$#vars]); } # check structure { my $pos_name = $pos->{name}; print "start[" . $pos->{name} . "]: @vars\n" if ($HNS::Hnf::TreeBuilder::debug); # check allowed if (!$pos->allowed($cmd_name)){ if ($pos_name ne $cmd_name && !$pos->OmittableEnd){ $pos->PushContent(HNS::Hnf::Warning::Message('NoMatch', $cmd_name, $pos_name)); } # complete end command automatically if omittable $self->end($cmd_name); print "end: $pos_name,", $pos->{name}, ",", $e->{name}, ",", $e->{parent}->{name}, "\n" if ($HNS::Hnf::TreeBuilder::debug); } # CAT must have only one New element if ($e->IsBeginSection && $pos->{name} ne 'CAT'){ print $e->{name}, "\n" if ($HNS::Hnf::TreeBuilder::debug); $self->{top}->InsertCommand('CAT'); } print "$cmd_name in $pos_name\n" if ($HNS::Hnf::TreeBuilder::debug); } # insert new command $self->{top}->InsertCommand($e); # if oneline command, insert end tag immidiately if ($e->IsOneline){ print "oneline:$cmd_name\n" if ($HNS::Hnf::TreeBuilder::debug); $self->end($cmd_name); } } # private: =head2 $t->end($cmd_name); コマンド $cmd_name の終わりを指定する。 現在ノードを移動。 =cut sub end($$) { my ($self, $cmd_name) = @_; print "end: $cmd_name\n" if ($HNS::Hnf::TreeBuilder::debug); my $p = $self->{top}->{pos} || $self; $cmd_name =~ s/^R?L(NEW|SUB)/$1/; # enclose until self tag while (defined $p){ my $p_name = $p->{name}; $p_name =~ s/^R?L(NEW|SUB)/$1/; # last if $p->{name} =~ /$cmd_name/; last if $p_name eq $cmd_name; $p = $p->{parent}; } $self->{top}->{pos} = (defined $p) ? $p->{parent} : $self->{top}->{pos}->{parent}; } =head2 $t->text($text); $text を現在位置へ push する =cut sub text($$) { my ($self, $text) = @_; my $pos = $self->{top}->{pos} || $self->{top}; print "text[" . $pos->{name} . "]:$text\n" if ($HNS::Hnf::TreeBuilder::debug); $pos->PushContent($text); } ################################################################ =head2 $t->Print($year, $month, $day, $section); 表示する =cut sub Print($$$$) { my ($self, $year, $month, $day, $section) = @_; print "TreeBuilder Print: $self, $year, $month, $day, $section\n" if ($HNS::Hnf::TreeBuilder::debug); my $html; # for footnote my $in_fn = 0; my @footnotes; # for group command my ($grpCount, $grpFlg, $tmpnewCount, $newDisplay); # for eval template my ($newCount, $subCount, $fnCount); my $abc; if ($day < 11) { $abc = "a"; } elsif ($day < 21) { $abc = "b"; } else { $abc = "c"; } my $code_params = q({year=>$year, month=>$month, day=>$day, high=>int($day/10), abc=>$abc, new=>$newCount, sub=>$subCount, fn=>$fnCount, cat=>$cat, mark=>$newDisplay}); my $grp; # category presentation my $cat; # traverse the tree-structure and print # FN, /FN and CAT is special command $self->{top}->Traverse(sub { my ($node, $start, $depth) = @_; if (ref $node){ # HNF command my $name = $node->{name}; if ($name eq 'FN'){ # FN command if ($start){ $fnCount++; } $in_fn = $start; # whether $node is content of FN HNS::Diary::HnfPrint ($node->AsHTML($start, eval($code_params))); } else { # else command if ($HNS::CategoryList::CatDir && $name eq 'CAT' && $start){ my $params = eval($code_params); $cat = get_category($params, $node->{arg_content}); } if ($in_fn){ $footnotes[$fnCount] .= $node->AsHTML($start, eval($code_params)); } else { my $count_name; if ($start && ($count_name = $node->CountName)){ if (($grpFlg) && ($name eq "NEW" || $name eq "LNEW" || $name eq "RLNEW")) { $grpFlg = 0; $newCount = $tmpnewCount - 1; } my $code = "\$${count_name}Count++;"; eval($code); $newDisplay = $newCount; # xhnf control code HNS::Diary::HnfPrint ("\n\n") if ($name eq "NEW" || $name eq "LNEW" || $name eq "RLNEW"); if ($grp) { HNS::Diary::HnfPrint ("\n"); $grpCount++; $tmpnewCount = $newCount; $newCount = "G" . $grpCount; $newDisplay = $HNS::Hnf::Command::GRP::Mark; $grpFlg = 1; $grp = undef; } } if ($name eq "GRP") { $cat = undef; $grp = $node->{arg_content}; } # beginning of section : NEW, SNEW print "parent:", $node->{parent}->{name} if ($HNS::Hnf::TreeBuilder::debug); print " node name:", $node->{name}, " start?:$start\n" if ($HNS::Hnf::TreeBuilder::debug); if (!$start && $node->IsBeginSection){ # clear count $subCount = 0; $fnCount = 0 if ($HNS::System::FNCountStyle == 0); # if current section has any FN content, # print it in here. if (@footnotes){ my $params = eval($code_params); print_footnote($params, @footnotes); # reset footnotes @footnotes = (); } } HNS::Diary::HnfPrint ($node->AsHTML($start, eval($code_params))); } } } else { # plain text if ($in_fn){ $footnotes[$fnCount] .= $node; } else { HNS::Diary::HnfPrint ($node); } }1;}); return; } sub get_category ($$) { my ($params, $arg_content) = @_; my $templ = new HNS::Template; $templ->SetParamValues(%$params); my $cat; # CAT arg1 arg2 arg3.. for my $c (split(/ /, $arg_content)){ my $img; my $enc_c = $c; $enc_c =~ s/([^a-zA-Z0-9~\-\_\.\/\:\%])/ sprintf("%%%02X",ord($1))/ge; if ($img = $HNS::CategoryList::DB{$c}){ my $src = "$HNS::CategoryList::CatDir/$img"; my ($width, $height) = imgsize($src); if ($width ne undef) { my $tmp = qq([$c]); $templ->SetTemplate($HNS::Hnf::Command::CAT::ImgTemplate); $templ->SetParamValues('enc_var'=>$enc_c); $templ->SetParamValues('img'=>$tmp); $cat .= $templ->Expand; } else { $templ->SetTemplate($HNS::Hnf::Command::CAT::Template); $templ->SetParamValues('var'=>$c); $templ->SetParamValues('enc_var'=>$enc_c); $cat .= $templ->Expand; } } elsif ($c) { $templ->SetTemplate($HNS::Hnf::Command::CAT::Template); $templ->SetParamValues('var'=>$c); $templ->SetParamValues('enc_var'=>$enc_c); $cat .= $templ->Expand; } } return $cat; } sub print_footnote ($@) { my ($params, @footnotes) = @_; my $templ = new HNS::Template; $templ->SetParamValues(%$params); # fn header $templ->SetTemplate ($HNS::Hnf::Command::FN::HeaderTemplate); HNS::Diary::HnfPrint ($templ->Expand); # fn content my $cnt; for (@footnotes[1..$#footnotes]){ $cnt++; next if (($_ eq "") && ($HNS::System::FNCountStyle)); $templ->SetTemplate ($HNS::Hnf::Command::FN::ContentTemplate); $templ->SetParamValues('fn'=>$cnt); $templ->SetParamValues('content'=>$_); HNS::Diary::HnfPrint ($templ->Expand); } # fn footer $templ->SetTemplate ($HNS::Hnf::Command::FN::FooterTemplate); HNS::Diary::HnfPrint ($templ->Expand); } 1;