XSLT3.0への道(6) xsl:mode

東日本大震災で被災された方に心よりお見舞いを申し上げます.
今回はxsl:modeを紹介します.私は最初ちょっと眺めただけでxsl:modeはストリーミングをサポートするために付け加わったものと思っていましたがそうではないようです.今までmode属性は、例えば、

<xsl:template match="*" mode="COPY">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-template mode="COPY"/>
  </xsl:copy>
</xsl:template>
 
のように結構適当に使ってきました.このmode属性に指定した名前を定義する命令はなかったわけです.xsl:modeは次のようなフォーマットで、この「モード」の様々な機能設定を行えます.
 
<xsl:mode
  name? = qname
  streamable? = "yes" | "no"
  initial? = "yes" | "no"
  on-no-match? = "stringify" | "discard" | "copy" | "fail"
  on-multiple-match? = "use-last" | "fail"
  warning-on-no-match? = "yes" | "no"
  warning-on-multiple-match? = "yes" | "no" >
  <!-- Content: (xsl:context-item?) -->
</xsl:mode>
 
streamableはストリーミングを規定します.残念ながらSaxon PEでは使えないので、残りを解説します.
 
■ name
nameはモードの名前です.例えばname="COPY"とすれば先ほどのmode="COPY"の動作を規定することができます.もしnameが指定されなければ、「名前なしモード(unnamed mode)」で、xsl:templateやxsl:apply-templatesでmode属性を使用しない通常のモードの動作を規定します.
 
■ initial
もしスタイルシートが、名前なしモードのxsl:templateやxsl:apply-templatesを1つも持っていないとき、initial="yes"のモードがデフォルトで動作します.(あまりそういうスタイルシートの作り方はしないと思いますが...)
 
■ on-no-match
マッチするxsl:templateが無かったときのビルトインテンプレートの処理ルールを規定します.on-no-match="stringfy"はXSLT1.0と同じで、要素ではselect属性なしのxsl:apply-templateの呼び出しとなり、テキストノードや属性ノードはそのテキストの出力となります.on-no-match="discard"は、結果ツリーに何も残しません.on-no-match="copy"は、入力ツリーが結果ツリーにそのままコピーされます.on-no-match="fail"は、このような場合エラー扱いとなります.
 
■ on-multiple-match
このエラーはDITAのスタイルシートを作るときによくお目にかかります.ノードが同じpriorityで複数のtemplateのmatch条件に合致してしまった場合の処理を規定します.on-multiple-match="use-last"は後に定義されたテンプレートを採用します.on-multiple-match="fail"は回復不可能なエラーとなります.
 
■ warning-on-no-match
xsl:apply-templatesがどのxsl:templateにもマッチしないとき、エラーを出すか否かを"yes","no"で指定します.
 
■ warning-on-multiple-match
ノードが同じpriorityで複数のtemplateのmatch条件に合致してしまった場合、警告を出すか否かを"yes","no"で指定します.
 
名前なしモードでxsl:modeを使ってみた例を示します.
 
[入力ファイル]
<?xml version="1.0" encoding="UTF-8" ?>
<data>
    <product type="pencil" maker="三菱"     price="100" count="5"/>
    <?comment Below data element is commentted. ?>
    <!--product type="pencil" maker="コーリン" price="90"  count="10"/-->
</data>
 
[スタイルシート]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 exclude-result-prefixes="xs"
>
<xsl:output indent="yes"/>
<xsl:mode streamable="no"
          on-no-match="copy"
          on-multiple-match="use-last"
          warning-on-no-match="yes"
          warning-on-multiple-match="yes">
</xsl:mode>
<xsl:template match="/">
    <xsl:apply-templates/>
</xsl:template>
<xsl:template match="*[not(@extra)]">
    <xsl:message select="'Former template is adopted!'"/>
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>
<xsl:template match="*[not(@dummy)]">
    <xsl:message select="'Latter template is adopted!'"/>
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>
 
[ログ]
Recoverable error
  XTRE0540: Ambiguous rule match for /data
Matches both "*[not(@dummy)]" on line 27 of
  file:/C:/Documents%20and%20Settings/toshi/style.xsl
and "*[not(@extra)]" on line 19 of
  file:/C:/Documents%20and%20Settings/toshi/style.xsl
Latter template is adopted!
Warning: No user-defined template rule found for node /data/text()[1]
Recoverable error
  XTRE0540: Ambiguous rule match for /data/product[1]
Matches both "*[not(@dummy)]" on line 27 of
  file:/C:/Documents%20and%20Settings/toshi/style.xsl
and "*[not(@extra)]" on line 19 of
  file:/C:/Documents%20and%20Settings/toshi/style.xsl
Latter template is adopted!
Warning: No user-defined template rule found for node /data/text()[2]
Warning: No user-defined template rule found for node /data/processing-instructi
on()[1]
Warning: No user-defined template rule found for node /data/text()[3]
Warning: No user-defined template rule found for node /data/comment()[1]
Warning: No user-defined template rule found for node /data/text()[4]
 
on-no-match="copy"を指定してあるので、結果のXMLは入力のXMLと同じです.
 
ログを見てわかりますが結構にぎやかになりました.規模の大きなスタイルシートを作成するときモードを結構な数使って組み上げることがあります、xsl:modeはそのようなときにデバッグするのに力になるでしょう.