XSLT3.0への道(26) パッケージへの期待

XSLT 3.0ではパッケージが導入されます.そしてどうもパッケージについては、いろいろと勉強しなければならないことが多そうです.

まず初めにXSLT 2.0までのXSLTスタイルシートの開発方法について振り返ってみました.例えばDITA-OTのプラグインスタイルシートを作るとき(仮にXSL-FOを生成するスタイルシートならば)、次のようなシェルのモジュールを作るのが一般的と思います.

[シェルモジュール]
<xsl:stylesheet version="2.0" 
   <!--自分の作ったメインのスタイルシートはインポートする-->
   <xsl:import href="dita2fo_import.xsl"/>
   <!--次に他の人がカスタマイズするスタイルシートをインポートする-->
   <xsl:import href="../customization/dita2fo_custom.xsl"/>
</xsl:stylesheet>

[メインのスタイルシート: dita2fo_import.xsl]

<?xml version='1.0' encoding="UTF-8" ?>
<xsl:stylesheet version="2.0" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:include href="dita2fo_main.xsl"/>
   <xsl:include href="dita2fo_constants.xsl"/>
   <xsl:include href="dita2fo_global.xsl"/>
   <xsl:include href="dita2fo_param.xsl"/>
    ...
</xsl:stylesheet>

どうしてこのようなことをやるかというと、プラグインは常にカスタマイズされるものという宿命を持っていると考えるからです.自分専用のスタイルシートを作るのだったら、xsl:importは必要としません.

そしてよくやってきたと思うのですが、XSLT 2.0におけるスタイルシート開発は2つの致命的な問題を抱えていました.

・ トップレベルのxsl:variableはすべてモジュール間に渡ってグローバルです.モジュールに閉じた変数というのは作ることができませんでした.
・ 同様に、テンプレートや関数もすべてグローバルです.モジュールの中に閉じたテンプレートや、関数は作ることができませんでした.

しかし、これらの問題は近代の多くの一般的なプログラミング言語ではなかったと思います.やはり、このようにすべてがグローバルだととかく不都合も生まれざるを得ません.いつ変数名やテンプレート、関数名がxsl:includeで取り込まれたモジュール間で衝突をおこすかわからないからです.(xsl:importされているモジュール間では、インポートの優先順位で衝突が解決されます.(どれか一つが採用されるはずです.)

また、xsl:importでは次のような問題がありました.

[入力XMLファイル:DITAを想定]
<?xml version="1.0" encoding="UTF-8"?>
<dita-doc>
    <ph class="- topic/ph ">Phrase text</ph>
    <b class="+ topic/ph hi-d/b ">Bold text</b>
</dita-doc>

[インポートされる側のモジュール]
    <xsl:template match="*[contains(@class,' topic/ph ')]">
        <fo:inline>
            <xsl:apply-templates/>
        </fo:inline>
    </xsl:template>

    <xsl:template match="*[contains(@class,' hi-d/b ')]" priority="2">
        <fo:inline font-weight="bold">
            <xsl:apply-templates/>
        </fo:inline>
    </xsl:template>

とあったときインポートする側で

    <xsl:template match="*[contains(@class,' topic/ph ')]">
        <fo:inline>
            <xsltext>ph: </xsl:text>
            <xsl:apply-templates/>
        </fo:inline>
    </xsl:template>

とすると、インポートする側のテンプレートが優先順位が高くなってしまい.<xsl:template match="*[contains(@class,' hi-d/b ')]" priority="2">の動くことは「永久に」なくなってしまうのです.これは結構問題になっていて、インポートされる側のモジュールをインポートする方にコピーしてxsl:includeで持ってきて書き換えない限り解決できないとった議論を聞いた覚えがあります.

実は私が期待するのはこのような点なのでした.xsl:importは超便利で、ものによっては効果的に使えるのですけれども、DITAの階層的な@class属性に沿った
テンプレートで必ず起こるこのような問題になんとかxsl:package/xsl:use-packageが役に立ってくれないか?という淡い期待があったからです.

実はこの期待はXSLT 3.0をもってしてももうまく行ってくれないようなのですが、次にそこまでたどり着いたアレコレの失敗談を紹介したいと思います.