XSLT3.0への道(31) パッケージを試してみる(その2)

ちょっと進んで(?)次のように複数のパッケージを試してみます.
[test-pkg-xslt3.xsl]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:package xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:fo="http://www.w3.org/1999/XSL/Format"
    xmlns:ahf="http://www.antennahouse.com/Names/XSLT/Document"
    package-version="1.0"
    name="urn:test:pkg:xslt3"
    exclude-result-prefixes="xs ahf"
    version="3.0"
    default-mode="normal">
   
    <xsl:mode name="normal" visibility="public"/>
    <xsl:mode/>
   
    <xsl:template match="*[contains(@class,' topic/ph ')]" name="tplPh">
        <fo:inline>
            <xsl:apply-templates/>
        </fo:inline>
    </xsl:template>
    <xsl:template match="*[contains(@class,' hi-d/b ')]" priority="2" name="tplB">
        <fo:inline font-weight="bold">
            <xsl:apply-templates/>
        </fo:inline>
    </xsl:template>
   
    <xsl:function name="ahf:setRed" as="attribute()*" visibility="public">
        <xsl:attribute name="color" select="'red'"/>
    </xsl:function>
   
</xsl:package>
[test-pkg2-xslt3.xsl]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:package xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:fo="http://www.w3.org/1999/XSL/Format"
    xmlns:ahf="http://www.antennahouse.com/Names/XSLT/Document"
    package-version="1.0"
    name="urn:test:pkg2:xslt3"
    exclude-result-prefixes="xs ahf"
    version="3.0"
    default-mode="normal">
   
    <xsl:mode name="normal" visibility="public"/>
    <xsl:mode/>
    <xsl:template match="*[contains(@class,' hi-d/i ')]" priority="2" name="tplI">
        <fo:inline font-style="italic">
            <xsl:apply-templates/>
        </fo:inline>
    </xsl:template>
   
</xsl:package>
[test-pkg-main2-xslt3.xsl]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:package xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    xmlns:fo="http://www.w3.org/1999/XSL/Format"
    xmlns:ahf="http://www.antennahouse.com/Names/XSLT/Document"
    exclude-result-prefixes="xs math ahf"
    name="urn:test:pkg:main:xslt3"
    default-mode="normal"
    version="3.0"
    >
   
    <xsl:use-package name="urn:test:pkg:xslt3"/>
    <xsl:use-package name="urn:test:pkg2:xslt3"/>
   
    <xsl:mode name="normal" visibility="public"/>
   
    <xsl:template match="/*">
        <fo:block>
            <xsl:copy-of select="ahf:setRed()"/>
            <xsl:apply-templates mode="normal"/>
        </fo:block>
    </xsl:template>
   
</xsl:package>
大きな意味はありません.、単純に従来機能単位にモジュールで記述していたものをxsl:packageにしたらどうなるか?を試しただけです.これをSaxon 9.8の最新版で実行すると、次のようにあえなくエラーではじかれます.

C:\Users\toshi\OneDrive\Documents\test\xslt\20170718-package>java -jar D:\My_Documents\Java\SaxonEE9-8-0-3J\saxon9ee.jar
 -s:input.xml -o:output-saxon.xml -xsl:test-pkg-main2-xslt3.xsl -config:saxon-config.xml -t
Saxon-EE 9.8.0.3J from Saxonica
Java version 1.8.0_60
Using license serial number V005977
Static error at xsl:mode on line 16 column 50 of test-pkg-main2-xslt3.xsl:
  XTSE3050: Mode normal conflicts with a public mode declared in package urn:test:pkg:xslt3
Errors were reported during stylesheet compilation

これはnormalと名付けられたモードが、競合するというメッセージです.
あれ?なんでこんな解釈になってしまうか?とXSLT 3.0の仕様を追うと次のようになっています.

1. まずパッケージで定義されるモードはコンポーネントという概念の一つに考えられます.
"The components which can be declared in one package and referenced in another are: functions, named templates, attribute sets, modes, and global variables and parameters."

2.2つのコンポーネントは同じ識別子を持つとき同名(homonymous)とされます.
"[Definition: Two components are said to be homonymous if they have the same symbolic identifier.]"

3.xsl:packageの中で使っているxsl:packageとコンポーネントが同名になるのは静的エラーです.
"[ERR XTSE3050] It is a static error if the xsl:use-package elements in a package manifest cause two or more homonymous components to be accepted with a visibility other than hidden."

まあ関数名なんかが同じ名前(ネームスペースも含め)なら確かに競合と言われてもおかしくないのですがmodeが同じ名前ではダメということは、xsl:apply-templatesやxsl:templateで指定するmode属性は、xsl:packageをまたがって共通にはできないということを意味しています.
この影響は非常に大きいと思います.私たちはスタイルシートをつくるときxsl:apply-templatesは

* 実は私はこの子ノードを処理する呼び出しの先のxsl:templateがどのようにインプリメントされているかは知りません.
* しかし、完成されたスタイルシートならばどこかのスタイルシートモジュールで、適切なxsl:templateが定義されているのでしょう.
* もし定義されていなければビルトインテンプレートが起動されることに異存はありません.

という前提です.だからこそXML文書を処理するスタイルシートは、いくつものスタイルシートモジュールに分割して開発が可能でした.ところがxsk:packageはこのような考え方には立脚していません.

この問題でxsl-listに疑問をポストしてみました.

XSLT 3.0のエディタのMichael Kay博士の基本的な考え方は次のようなものです.

The thinking is that if package A uses packages B and C, then B and C were
developed and tested independently and have no knowledge of each other or
dependence on each other. When a template rule in B calls apply-templates,
it's not expecting a template rule in C to be invoked, because it was written
with no knowledge of C.

「パッケージAがパッケージB,Cを使用していて、これらは互いに内部を知ることはなく独立して開発されている場合、Bの中でのxsl:apply-templatesはCのxsl:templateを呼び出すことは期待されない.そもそもBはCの何たるかを知ることなく開発されているからである.」

つまりxsl:packageは非常に独立性の高いことが想定されていて、その中でのxsl:apply-templatesは「中に閉じて処理」されます.
このような前提自体はおかしくはないのですが、残念ながら文書XMLを処理するテンプレートには適用することはまず無理でしょう.何故ならそれは文書モデル自体が再帰的に定義されているからです.例えば

* 段落(p)はテキストだkでなく、表(table)やol,ul(番号付きリスト、番号なしリスト)を内部にコンテンツとして持つことができます.
* ol,ul/liは段落(p)を子要素として持つことができます.
* 表のセル(entry)は、段落(p)を子要素に持つことができます.

従って、モジュール分割をして、段落を処理するスタイルシートモジュール、リスト(ol.ul)を処理するスタイルシートモジュール、表(table)を処理するスタイルシートモジュールを作ってもそれらは決して独立ではなく、xsl:apply-templatesで互いに「呼び出し合う」結果となります.

ですので、このような構造に根差したテンプレートをxsl:packageで記述しようとすれば、すべての互いに呼び出し合う可能性のあるxsl:templateを一つのxsl:packageに入れなければならないという結果になってしまいます.これは到底受け入れることはできません.

という訳で、非常に期待していたxsl:packageは少なくとも文書XMLを処理するスタイルシートには残念ながら使用することができないことがわかりました.これはとてももったいない(?)話なので、Kay博士がこの辺の事情を斟酌(忖度?)してくれてXSLT 3.1で文書XMLを処理するスタイルシートでも使えるxsl:packageの仕様を考えてくれたらと心の底から期待しています.