################################################################################ #

# Wikiフォーマットの文字列をパースし、書式に対応したフックメソッドの呼び出しを行います。 # Wiki::Parserを継承し、これらのフックメソッドをオーバーライドすることで任意のフォーマットへの変換が可能です。 #

################################################################################ package Wiki::Parser; use strict; use Wiki::Keyword; use Wiki::InterWiki; $Wiki::Parser::keyword = undef; $Wiki::Parser::interwiki = undef; #=============================================================================== #

# コンストラクタ。 #

#
# my $parser = Wiki::HTMLParser->new($wiki);
# 
#=============================================================================== sub new { my $class = shift; my $wiki = shift; my $self = {}; $self->{wiki} = $wiki; # KeywordとInterWikiは高速化のためモジュール変数として保持する #(ただしmod_perl+Farmの場合はダメなので毎回newする) if(exists $ENV{MOD_PERL}){ $self->{interwiki} = Wiki::InterWiki->new($wiki); $self->{keyword} = Wiki::Keyword->new($wiki,$self->{interwiki}); } else { unless(defined($Wiki::Parser::keyword)){ $Wiki::Parser::interwiki = Wiki::InterWiki->new($wiki); $Wiki::Parser::keyword = Wiki::Keyword->new($wiki,$Wiki::Parser::interwiki); } $self->{interwiki} = $Wiki::Parser::interwiki; $self->{keyword} = $Wiki::Parser::keyword; } $self->{dl_flag} = 0; $self->{dt} = ""; $self->{dd} = ""; return bless $self,$class; } #=============================================================================== #

# パース処理を開始します。 #

#
# $parser->parse($source);
# 
#=============================================================================== sub parse { my $self = shift; my $source = shift; $self->start_parse; $source =~ s/\r//g; my @lines = split(/\n/,$source); foreach my $line (@lines){ chomp $line; # 複数行の説明 $self->multi_explanation($line); my $word1 = substr($line,0,1); my $word2 = substr($line,0,2); my $word3 = substr($line,0,3); # 空行 if($line eq ""){ $self->l_paragraph(); next; } # パラグラフプラグイン if($line =~ /^{{((.|\s)+?)}}$/){ my $plugin = $self->{wiki}->parse_inline_plugin($1); my $info = $self->{wiki}->get_plugin_info($plugin->{command}); if($info->{TYPE} eq "paragraph"){ $self->l_plugin($plugin); } else { my @obj = $self->parse_line($line); $self->l_text(\@obj); } next; } # PRE if($word1 eq " " || $word1 eq "\t"){ $self->l_verbatim($line); # 見出し } elsif($word3 eq "!!!"){ my @obj = $self->parse_line(substr($line,3)); $self->l_headline(1,\@obj); } elsif($word2 eq "!!"){ my @obj = $self->parse_line(substr($line,2)); $self->l_headline(2,\@obj); } elsif($word1 eq "!"){ my @obj = $self->parse_line(substr($line,1)); $self->l_headline(3,\@obj); # 項目 } elsif($word3 eq "***"){ my @obj = $self->parse_line(substr($line,3)); $self->l_list(3,\@obj); } elsif($word2 eq "**"){ my @obj = $self->parse_line(substr($line,2)); $self->l_list(2,\@obj); } elsif($word1 eq "*"){ my @obj = $self->parse_line(substr($line,1)); $self->l_list(1,\@obj); # 番号付き項目 } elsif($word3 eq "+++"){ my @obj = $self->parse_line(substr($line,3)); $self->l_numlist(3,\@obj); } elsif($word2 eq "++"){ my @obj = $self->parse_line(substr($line,2)); $self->l_numlist(2,\@obj); } elsif($word1 eq "+"){ my @obj = $self->parse_line(substr($line,1)); $self->l_numlist(1,\@obj); # 水平線 } elsif($line eq "----"){ $self->l_line(); # 引用 } elsif($word2 eq '""'){ my @obj = $self->parse_line(substr($line,2)); $self->l_quotation(\@obj); # 説明 } elsif(index($line,":")==0 && index($line,":",1)!=-1){ if(index($line,":::")==0){ $self->{dd} .= substr($line,3); next; } if(index($line,"::")==0){ if($self->{dt} ne "" || $self->{dd} ne ""){ $self->multi_explanation; } $self->{dt} = substr($line,2); $self->{dl_flag} = 1; next; } my $dt = substr($line,1,index($line,":",1)-1); my $dd = substr($line,index($line,":",1)+1); my @obj1 = $self->parse_line($dt); my @obj2 = $self->parse_line($dd); $self->l_explanation(\@obj1,\@obj2); # テーブル } elsif($word1 eq ","){ if($line =~ /,$/){ $line .= " "; } my @spl = map {/^"(.*)"$/ ? scalar($_ = $1, s/\"\"/\"/g, $_) : $_} ($line =~ /,\s*(\"[^\"]*(?:\"\"[^\"]*)*\"|[^,]*)/g); my @array; foreach my $value (@spl){ my @cell = $self->parse_line($value); push @array,\@cell; } $self->l_table(\@array); # コメント } elsif($word2 eq "//"){ # 何もない行 } else { my @obj = $self->parse_line($line); $self->l_text(\@obj); } } # 複数行の説明 $self->multi_explanation; $self->end_parse; } #=============================================================================== #

# 複数行の説明文を処理します。 #

#=============================================================================== sub multi_explanation { my $self = shift; my $line = shift; if($self->{dl_flag}==1 && (index($line,":")!=0 || !defined($line))){ my @obj1 = $self->parse_line($self->{dt}); my @obj2 = $self->parse_line($self->{dd}); $self->l_explanation(\@obj1,\@obj2); $self->{dl_flag} = 0; $self->{dt} = ""; $self->{dd} = ""; } } #=============================================================================== #

# 1行分をパースします。parseメソッドの中から必要に応じて呼び出されます。 #

#=============================================================================== sub parse_line { my $self = shift; my $source = shift; my @array = (); # プラグイン if($source =~ /{{((.|\s)+?)}}/){ my $pre = $`; my $post = $'; if($pre ne ""){ push(@array,$self->parse_line($pre)); } my $plugin = $self->{wiki}->parse_inline_plugin($1); my $info = $self->{wiki}->get_plugin_info($plugin->{command}); if($info->{TYPE} eq "inline"){ push @array,$self->plugin($plugin); } else { push @array,$self->text("{{$1}}"); } if($post ne ""){ push(@array,$self->parse_line($post)); } # ボールド、イタリック、取り消し線、下線 } elsif($source =~ /((''')|('')|(==)|(__))(.+?)(\1)/){ my $pre = $`; my $post = $'; my $type = $1; my $label = $6; if($pre ne ""){ push(@array,$self->parse_line($pre)); } if($type eq "'''"){ push @array,$self->bold($label); } elsif($type eq "__"){ push @array,$self->underline($label); } elsif($type eq "''"){ push @array,$self->italic($label); } elsif($type eq "=="){ push @array,$self->denialline($label); } if($post ne ""){ push(@array,$self->parse_line($post)); } # InterWikiName } elsif($self->{interwiki}->exists_interwiki($source)){ my $pre = $self->{interwiki}->{g_pre}; my $post = $self->{interwiki}->{g_post}; my $label = $self->{interwiki}->{g_label}; my $url = $self->{interwiki}->{g_url}; if($pre ne ""){ push(@array,$self->parse_line($pre)); } push @array,$self->url_anchor($url,$label); if($post ne ""){ push(@array,$self->parse_line($post)); } # ページ別名リンク } elsif($source =~ /\[\[([^\[]+?)\|(.+?)\]\]/){ my $pre = $`; my $post = $'; my $label = $1; my $page = $2; if($pre ne ""){ push(@array,$self->parse_line($pre)); } push @array,$self->wiki_anchor($page,$label); if($post ne ""){ push(@array,$self->parse_line($post)); } # URL別名リンク } elsif($source =~ /\[([^\[]+?)\|((http|https|ftp|mailto):[a-zA-Z0-9\.,%~^_+\-%\/\?\(\)!\$&=:;\*#\@']*)\]/ || $source =~ /\[([^\[]+?)\|(file:[^\[\]]*)\]/ || $source =~ /\[([^\[]+?)\|((\/|\.\/|\.\.\/)+[a-zA-Z0-9\.,%~^_+\-%\/\?\(\)!\$&=:;\*#\@']*)\]/){ my $pre = $`; my $post = $'; my $label = $1; my $url = $2; if($pre ne ""){ push(@array,$self->parse_line($pre)); } if(index($url,'"') >= 0 || index($url,'><') >= 0 || index($url, 'javascript:') >= 0){ push @array,"不正なリンクです。"; } else { push @array,$self->url_anchor($url,$label); } if($post ne ""){ push(@array,$self->parse_line($post)); } # URLリンク } elsif($source =~ /(http|https|ftp|mailto):[a-zA-Z0-9\.,%~^_+\-%\/\?\(\)!\$&=:;\*#\@']*/ || $source =~ /\[([^\[]+?)\|(file:[^\[\]]*)\]/){ my $pre = $`; my $post = $'; my $url = $&; if($pre ne ""){ push(@array,$self->parse_line($pre)); } if(index($url,'"') >= 0 || index($url,'><') >= 0 || index($url, 'javascript:') >= 0){ push @array,"不正なリンクです。"; } else { push @array,$self->url_anchor($url); } if($post ne ""){ push(@array,$self->parse_line($post)); } # ページリンク } elsif($source =~ /\[\[([^\|]+?)\]\]/){ my $pre = $`; my $post = $'; my $page = $1; if($pre ne ""){ push(@array,$self->parse_line($pre)); } push @array,$self->wiki_anchor($page); if($post ne ""){ push(@array,$self->parse_line($post)); } # 任意のURLリンク } elsif($source =~ /\[([^\[]+?)\|(.+?)\]/){ my $pre = $`; my $post = $'; my $label = $1; my $url = $2; if($pre ne ""){ push(@array,$self->parse_line($pre)); } if(index($url,'"') >= 0 || index($url,'><') >= 0 || index($url, 'javascript:') >= 0){ push @array,"不正なリンクです。"; } else { # URIを作成 my $wiki = $self->{wiki}; my $uri = $wiki->config('server_host'); if($uri eq ""){ $uri = $wiki->get_CGI()->url(-path_info => 1); } else { $uri = $uri . $wiki->get_CGI->url(-absolute => 1) . $wiki->get_CGI()->path_info(); } push @array,$self->url_anchor($uri."/../".$url, $label); } if($post ne ""){ push(@array,$self->parse_line($post)); } # キーワード } elsif($self->{keyword}->exists_keyword($source)){ my $pre = $self->{keyword}->{g_pre}; my $post = $self->{keyword}->{g_post}; my $label = $self->{keyword}->{g_label}; my $url = $self->{keyword}->{g_url}; my $page = $self->{keyword}->{g_page}; if($pre ne ""){ push(@array,$self->parse_line($pre)); } if(defined($url) && $url ne ""){ push @array,$self->url_anchor($url,$label); } else { push @array,$self->wiki_anchor($page,$label); } if($post ne ""){ push(@array,$self->parse_line($post)); } # WikiName } elsif($self->{wiki}->config('wikiname')==1 && $source =~ /[A-Z]+?[a-z]+?([A-Z]+?[a-z]+)+/){ my $pre = $`; my $post = $'; my $page = $&; if($pre ne ""){ push(@array,$self->parse_line($pre)); } push @array,$self->wiki_anchor($page); if($post ne ""){ push(@array,$self->parse_line($post)); } # エラーメッセージ } elsif($source =~ /(<<)(.+?)(>>)/){ my $pre = $`; my $post = $'; my $label = $2; if($pre ne ""){ push(@array,$self->parse_line($pre)); } push @array,$self->error($label); if($post ne ""){ push(@array,$self->parse_line($post)); } } else { push @array,$self->text($source); } return @array; } #=============================================================================== #

# パースを開始前に呼び出されます。 # サブクラスで必要な処理がある場合はオーバーライドしてください。 #

#=============================================================================== sub start_parse {} #=============================================================================== #

# パース終了後に呼び出されます。 # サブクラスで必要な処理がある場合はオーバーライドしてください。 #

#=============================================================================== sub end_parse {} #=============================================================================== #

# URLアンカにマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub url_anchor {} #=============================================================================== #

# ページ名アンカにマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub wiki_anchor {} #=============================================================================== #

# イタリックにマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub italic {} #=============================================================================== #

# ボールドにマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub bold {} #=============================================================================== #

# 下線にマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub underline {} #=============================================================================== #

# 打ち消し線にマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub denialline {} #=============================================================================== #

# プラグインにマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub plugin {} #=============================================================================== #

# テキストにマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub text{} #=============================================================================== #

# 項目にマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub l_list {} #=============================================================================== #

# 番号付き項目にマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub l_numlist {} #=============================================================================== #

# 見出しにマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub l_headline {} #=============================================================================== #

# PREタグにマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub l_verbatim {} #=============================================================================== #

# 水平線にマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub l_line {} #=============================================================================== #

# 特になにもない行にマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub l_text {} #=============================================================================== #

# 説明にマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub l_explanation {} #=============================================================================== #

# 引用にマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub l_quotation {} #=============================================================================== #

# パラグラフの区切りにマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub l_paragraph {} #=============================================================================== #

# テーブルにマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub l_table {} #=============================================================================== #

# パラグラフプラグインにマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub l_plugin {} #=============================================================================== #

# 画像にマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub l_image {} #=============================================================================== #

# エラーメッセージにマッチした場合に呼び出されます。 # サブクラスにて処理を実装します。 #

#=============================================================================== sub error {} 1;