DITA-OTでのSaxonの起動方法

今のDITA-OTではXSLTプロセッサはSaxon-B 9.1というXSLT2.0プロセッサが標準になっています.昔はSaxon 6.5やXalan 2.7.1というXSLT 1.0のプロセッサが標準でした.最新のSaxonは9.5なのでもうちょっと古いバージョンになります.何故このバージョンになっているかというと、次のSaxon9.2からHE(Home Edition)とPE(Prpfessional Edition)/EE(Enterprise Edition)と分かれてしまい、オープンソースのHEバージョンではスタイルシートからJavaの拡張ライブラリが呼べなくなってしまったからです.DITA-OTではJavaのライブラリ呼び出しを使っています.こういう仕組みを無償で使えるSaxonは9.1が最後のためだからです.oXygenなどのXMLエディタではたいてい最新のSaxon EEがついてくるので、oXygenを使っている限りはXSLT3.0でスタイルシートを書くことだって可能です.もちろんSaxon PE/EEを買ってDITA-OTに組み込めば使えるXSLTの機能はずっとアップします.

ところでこのSaxonをantのbuild.xmlから呼び出すには2つのやり方があります.

1.xsltタスクを用いる、つまりJAXP(Java API for XML Processing)を使ってSaxonを呼び出す.
2.javaタスクを用いる、つまりSaxonのメインクラスのnet.sf.saxon.Transformを指定してSaxonを呼び出す.

私は従来は2.を使用していました.理由はスタイルシートで次のようにsyspropertyを使用したいのですが、

<sysproperty key="use.i18n.index.lib" value="${use.i18n.index.lib}"/>

この機能がantの1.8.0以前はxsltタスクでは使えなかったからです.1.と2.を比べてみるとスタイルシートのインタフェースでは、1の場合

<param name="PRM_OUTPUT_DRAFT_COMMENT" expression="${output.draft.comment}" if="output.draft.comment"/>

と条件付(output.draft.commentプロパティが定義されていた場合のみ)パラメータでプロパティを渡すことができます.でもjavaタスクではこのようなことはできず、

<arg line='"PRM_OUTPUT_DRAFT_COMMENT=${output.draft.comment}"'/>

とパラメータをハードコードしなければ書かねばなりません.このコマンドラインには癖があって、"="の右側が空だとエラーになってしまいます.つまりパラメータは必ず値を書かねばなりません.これは結構重要な問題で、もしパラメータが定義されていない場合に備えて、

<property name="output.draft.comment" value="no"/>

などと初期値をbuild.xmlに指定することが必要になってきます.でももし当該のプラグインが業務でCMSのアプリケーションからお客様が起動するようになった場合、パラメータの初期値は下手をすると以下の3箇所にバラバラに存在してしまう結果になります.

(A) DITA-OTを起動するCMSのアプリケーション
(C) プラグインスタイルシートの持つ<xsl:param select="~"/>の値

CMSとインテグレーションする場合たいていトラブルが発生します.パラメータが渡されていないためプラグインの動作結果が意図通りにならない場合もあります.なので私は2.のbuild.xmlに初期値を持つことは極力やめて、CMSのアプリケーションからすべてのパラメータをもらった方が良いと考えています.そうしないとパラメータの値がどこでどう設定されるのかで、本番環境に組み込んだときに大変な思いをすること間違いないからです.

そうすると、プラグインxsltタスクでSaxonを起動するのがやはり良いでしょう.プラグインの中でのパラメータの初期値を除去し、CMS側からのパラメータをスルーできるからです.

でもJAXPとjavaコマンドラインでは微妙にSaxonの動作も異なるようです.少し私の経験をまとめてみました.

まずJAXPではjavaコマンドラインで動いていたコードが動かなくなる場合があります.

例えば次のコードはjavaコマンドラインでは$PRM_RESOURCE_FILE='D:/My_Documents/data/resource.xml'で動きます.

  <xsl:param name="PRM_RESOURCE_FILE" as="xs:string" required="yes"/>
  <xsl:variable name="pResourceFile" as="xs:string">
    <xsl:choose>
      <xsl:when test="doc-available($PRM_RESOURCE_FILE)">
        <xsl:sequence select="$PRM_RESOURCE_FILE"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message select="concat('$PRM_RESOURCE_FILE=',$PRM_RESOURCE_FILE,' がありません!')"/>
        <xsl:sequence select="''"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

でもJAXPでこのコードを動かすとdoc-available()はfalse()を返し、ファイルにアクセスすることができません.JAXPでは先頭に"file:/"をつけてちゃんとしたURIにしてやる必要があるようです.例えば次のようにコードを変えます.これはjavaでもxsltタスクでも動きます.

  <xsl:param name="PRM_RESOURCE_FILE" as="xs:string" required="yes"/>
  <xsl:variable name="pResourceFile" as="xs:string">
    <xsl:variable name="tempResourceFile" as="xs:string">
      <xsl:choose>
        <xsl:when test="starts-with($PRM_RESOURCE_FILE,'file:/')">
          <xsl:sequence select="$PRM_RESOURCE_FILE"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="concat('file:/',$PRM_RESOURCE_FILE)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="doc-available($tempResourceFile)">
        <xsl:sequence select="$tempResourceFile"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message select="concat('$PRM_RESOURCE_FILE=',$PRM_RESOURCE_FILE,' がありません!')"/>
        <xsl:sequence select="''"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

次はデバッグ方法の違いについて紹介します.