WikiTextことはじめ

先日のエントリで取り上げたWikiTextについて、ざっとまとめておきます。後人の誰かの役に立てば。


概要

WikiTextとは、Javaで書かれたWikiパーサです*1

Mylyn内部でWikiをパースする部分に使用されているので、知らず知らずのうちに恩恵を受けている人もいるでしょう*2Eclipse/Mylynとの連携だけでなくWikiText単体でも使用することができますので、各人のJavaアプリケーションにも組み込むことができます。単体で使用する場合には、MylynのダウンロードページからWikiTextのスタンドアローン版を取得できます。

デフォルトでMediaWikiTracなどのWiki記法と出力形式に対応していますが、(はてなダイアリーなど)対応していないWikiに対しても、自分で拡張してWikiパーサを実装することができます。

対応フォーマット

  • 入力フォーマット
フォーマット 説明 文法
Confluence Webベースの企業向けWiki Page Not Found
MediaWiki WikiPediaでも使われているWiki Help:Formatting/ja - MediaWiki
Textile RedmineのWiki記法のベースとなっている記法
TracWiki Tracで採用されているWiki WikiFormatting – The Trac Project
TWiki java.netなどで使用されているWiki TextFormattingRules < TWiki04 < TWiki

冒頭で述べたように、デフォルトで対応していなくとも自分で実装することによって、対応するWiki記法を増やすことができます。

  • 出力フォーマット
フォーマット 説明
HTML
DocBook XMLベースで技術文書のためのマークアップ言語。PDF出力可能。
DITA 役割的にはDocBookと似たようなもの?PDF出力可能。
Eclipse Help Format
XSL-FO Apache FOPとの組み合わせでPDF出力可能。

HTML出力には標準で対応していますが、残念ながらPDF出力は直接はサポートしていません。いったん、別の形式に出力してからPDFに落とすという形になります。私は、(XSL-FO ⇒ Apache FOP ⇒ PDF)というやり方でPDF出力を試してみました。

あと、出力の方は入力ほど楽に拡張できる仕組みは備わっていません、多分。もちろんソースは公開されているので、自力で拡張すれば何だってできるとは思いますがw

WikiTextの使用方法

WikiText Developer Guide - Using The WikiText Parserに、大まかな使用方法が記載されています。

Wikiのパースは↓の流れで行われます。

  1. DocumentBuilderで出力フォーマット(と出力先:Writer)を指定
    • 例:出力フォーマットをHTML
  2. MarkupParserのコンストラクタで入力対象のWiki言語を指定
    • 例:入力フォーマットをTextile
  3. MarkupParser#setBuilderでBuilderをセット
  4. MarkupParser#parseでパース
  5. Writerからパース結果を取得
StringWriter writer = new StringWriter();
HtmlDocumentBuilder builder = new HtmlDocumentBuilder(writer);

MarkupParser parser = new MarkupParser(new TextileLanguage());
parser.setBuilder(builder);
parser.parse(markupContent);

String htmlContent = writer.toString();

Builderの設定を変えることによって、出力を調整することができます。例として、CSSセット・HTML/BODYタグ出力無は、以下のようにします。HTML/BODYタグを出力しない設定は、他のアプリケーションに組み込む際に有用なオプションでしょう。

// Add a CSS stylesheet as <link type="text/css" rel="stylesheet" href="styles/test.css"/>
builder.addCssStylesheet("styles/test.css");

// avoid the <html> and <body> tags 
builder.setEmitAsDocument(false);

また、一番シンプルなHTML出力では、Builderの定義を省いてMarkupParser#parseToHtmlを使うこともできます。

MarkupParser parser = new MarkupParser(new TextileLanguage());
String htmlContent = parser.parseToHtml(markupContent);

WikiTextの拡張方法

多分、皆さんが一番興味あるところでしょう。実は、私も全部を追いきれたわけではありません。いかんせん、日本語・英語問わず情報が少なかったので…。WikiText Developer Guide - Markup Languagesを参考に試行錯誤した結果、何となくこんな感じでできますよという形で載せておきます。

  • Wiki言語のコンセプト

以下の3つが、Wiki言語を構成する要素です。

名称 HTML要素に相当するもの 範囲 重複 ネスト
Block DIV, P, LI など 複数行 しない 基本しない
Phrase SPAN など 単一行 しない あり得る
Replacement Token HyperLink など 単一行 しない しない
  • 拡張ポイント

ドキュメントには「org.eclipse.mylyn.wikitext.core.parser.markup.MarkupLanguage」をextendしてくださいとありますが、どちらかというとそのサブクラスである「AbstractMarkupLanguage」の方が拡張しやすいと感じました。

AbstractMarkupLanguageの抽象メソッドは以下の4つなので、拡張されるクラスでそれぞれ実装する必要があります。1, 2, 3は、上記コンセプトのそれぞれBlock, Phrase, Replacement Tokenに当たります。4は、パースする際に一番最初に呼ばれるみたい。

  1. addStandardBlocks
  2. addStandardPhraseModifiers
  3. addStandardTokens
  4. createParagraphBlock

参考までに、MediaWikiパーサの実際のソースコードを一部抜粋します。上記の抽象メソッドが実装されているのが分かるかと思います。

public class MediaWikiLanguage extends AbstractMarkupLanguage {
  ...

  @Override
  protected void addStandardBlocks(List<Block> blocks, List<Block> paragraphBreakingBlocks) {
    blocks.add(new HeadingBlock());
    ...
  }

  @Override
  protected void addStandardPhraseModifiers(PatternBasedSyntax phraseModifierSyntax) {
    phraseModifierSyntax.add(new SimplePhraseModifier("'''", SpanType.BOLD, true));
    ...
  }

  @Override
  protected void addStandardTokens(PatternBasedSyntax tokenSyntax) {
    tokenSyntax.add(new HyperlinkInternalReplacementToken());
    ...
  }

  @Override
  protected Block createParagraphBlock() {
    ParagraphBlock paragraphBlock = new ParagraphBlock(hasPreformattedBlock());
    ...
  }

 ...
}

Wikiパーサを自作する際のnew HeadingBlock()とかnew HyperlinkInternalReplacementToken()とかの実装は、自分で作成するよりはデフォルトで用意されているものを適宜参考にする方が一番手っ取り早いでしょう*3

たとえば、はてなダイアリーでの見出し記法は行頭に「*」で始まるものと定義されていますが、TWikiでの見出し記法は「---*」で始まると定義されています。ならば、TWikiのHeadingBlockをコピってきて該当部分から「---」を除外したパターンに修正してやることで、はてな記法での見出しを実現できます。


以上、WikiTextの概要から簡単な使用・拡張方法についてまとめてみました。

実際のところ、ある1つのWiki言語からHTMLに出力するだけならば、こういったパーサを使うまでもなく正規表現でゴリ×2replaceしてやればいいと思います。こういったパーサを使う利点は、多様な入力・出力フォーマットを汎用的に扱えることだと考えています。

入力フォーマット(つまりWiki言語)をコロコロ変更するということは実際の場面ではあまりないかもしれませんが、出力先を色々変えたいというのは十分あり得るのではと思います。例えば、HTMLだけでなくPDFに出したいとかOffice文書で出したいとかはありがちですよね。

もしそういった場面でWikiパーサを使おうと考えたときに、このエントリが何かしらの参考になれば幸いです。

*1:正確には、軽量マークアップ言語のパーサということですが、Wikiが一番わかりやすいでしょうから

*2:実はMylynもデモとかでしか見てなくて、実際に自分で使ったことはないのですが。何かアイデア出そうだから、近いうちに触ってみたいな。

*3:内部では、正規表現のパターンを記述することによって実現されています