テンプレートと変数(2)

XSLT2.0ではテンプレートと変数の構造は次のように定義されています.

6.1 Defining Templates

<!-- Category: declaration -->
<xsl:template
  match? = pattern
  name? = qname
  priority? = number
  mode? = tokens
  as? = sequence-type>
  <!-- Content: (xsl:param*, sequence-constructor) -->
</xsl:template>

9.1 Variables

<!-- Category: declaration -->
<!-- Category: instruction -->
<xsl:variable
  name = qname
  select? = expression
  as? = sequence-type>
  <!-- Content: sequence-constructor -->
</xsl:variable>

変数もテンプレートも、XSLT1.0との違いは大雑把に言えば

① as属性でシーケンスタイプを指定できるようになりました.
② templateとされていた本体部分がsequence-constructor(シーケンスコンストラクタ)に置き換えられました.

という違いです.ではここで出てきた「シーケンス」とはそもそも何か?ということになります.これもざっくり言えば

① シーケンスとはアイテムの配列です.アイテムがゼロ個の場合は空シーケンス(empty sequence)と呼ばれます.
② アイテムは総称的な型のインスタンスです.実際にはノード型かXML Schemaで規定されたアトミック型のインスタンスです.

となります.しかしこれだけではまだ何を言っているかピンと来ません.理解するためにはXPath2.0で導入された型システムを知る必要があります.

XPath1.0では、型は明示的に指定することはできず、種類も限定されたものでしたが、XPath2.0では次のような型システムが導入されました.

2.6.3 Type Hierarchy

item型を頂点として、左はnode型、右はXML Schemaで定義された組み込みのアトミック型(原始型:atomic type)です.

このように型システムがXPath1.0とは根本的に変わっています.

この前提の上に立てばsequence-constructorとは、これらの型のインスタンスを生み出すものと考えることが出来るでしょう.

つまり、XSLT1.0では、変数がtemplateの生成するノードを保持し、テンプレートはtemplateの生成するノードを結果ツリーに書き込むものでしたが、XSLT2.0では、変数はsequence-constructorの生成するシーケンスを保持し、テンプレートはsequence-constructorの生成するシーケンスを結果ツリーに書き込みます.

XSLT1.0ではtemplateが生成するものはノードだけだったのですが、sequence-constructorはもうノードという呪縛に縛られることはなく、ノード型でもアトミック型のインスタンスでも自由に生成できるようになったということが大きな特徴です.

これを具体的な例で見てみます.例えば、複雑なXPath式を書かざるを得ないとき、あらかじめ変数に条件判定の結果を格納しておき、xsl:choose/xsl:whenやxsl:ifではその変数の値により処理を分岐させ式を簡単にさせる処理をよく行います.

次の例は①先頭のchapterでは@page-break="no"の指定がない限りページブレークさせ、②先頭でないchapterでは@page-break="yes"のときだけページブレークさせるというXSLT1.0のコードです.

    <xsl:variable name="cTrue" select="'true'"/>
    <xsl:variable name="cFalse" select="'false'"/>
    
    <xsl:template match="chapter">
        <xsl:variable name="doPageBreak">
            <xsl:choose>
                <xsl:when test="not(preceding-sibling::chapter) and (string(@page-break) != 'no')">
                    <xsl:value-of select="$cTrue"/>
                </xsl:when>
                <xsl:when test="preceding-sibling::chapter and (string(@page-break) = 'yes')">
                    <xsl:value-of select="$cTrue"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$cFalse"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <fo:block>
            <xsl:if test="$doPageBreak = $cTrue">
                <xsl:attribute name="break-before">page</xsl:attribute>
            </xsl:if>
            <xsl:apply-templates/>
        </fo:block>
    </xsl:template>

変数$doPageBreak内のxsl:value-ofはテキストノードを生成します.これを<xsl:if test="$doPageBreak = $cTrue">で文字列型にキャストして比較しています.よくxsl:value-ofを、そのネーミングから何か「値を持ってくる命令」と誤解している方がいます.あくまでxsl:valu-ofはテキストノードを生成します.つまり<xsl:value-of select="$cTrue"/>は<xsl:text>true</xsl:text>と変わりません.

これをXSLT2.0で書くと次のようになります.

    <xsl:template match="chapter">
        <xsl:variable name="doPageBreak" as="xs:boolean">
            <xsl:choose>
                <xsl:when test="empty(preceding-sibling::chapter) and (string(@page-break) ne 'no')">
                    <xsl:sequence select="true()"/>
                </xsl:when>
                <xsl:when test="exists(preceding-sibling::chapter) and (string(@page-break) eq 'yes')">
                    <xsl:sequence select="true()"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:sequence select="false()"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <fo:block>
            <xsl:if test="$doPageBreak">
                <xsl:attribute name="break-before" select="'page'"/>
            </xsl:if>
            <xsl:apply-templates/>
        </fo:block>
    </xsl:template>

変数$doPageBreakはxs:boolean(XML Schemaで定義されている真偽値型)として宣言されます.注目していただきたいのは<xsl:sequenece>です.これはXSLT2.0で新規に導入された命令で、select属性の内容を、シーケンスとして生成してくれます.

11.10 Constructing Sequences

The xsl:sequence instruction may be used within a sequence constructor to construct a sequence of nodes and/or atomic values. This sequence is returned as the result of the instruction. 
The items comprising the result sequence are selected using the select attribute.

この例ではXSLT1.0がテキストノードを文字列にキャストして判定していたのに比べ、真偽値としてただちに<xsl:if test="$doPageBreak">とコーディングできます.

このように変数ではas属性で明示的に型を指定でき、その型に沿ってxsl:sequenceでシーケンスを生成できるようになりました.これが決定的なXSLT1.0からXSLT2.0へと変わった点です.

この事情は変数だけでなくテンプレートでも同じです.次はテンプレートの例を紹介します.