XSLT 2.0で便利になった機能(43) xsl:next-match

目からうろこという言葉がありますが、このxsl:next-matchはまさにそれでした.
 
今日お客様の仕事でDITAのスタイルシートのカスタマイズをしていました.お客様からのリクエストは、task/taskbody/stepsが出現したら、そこに「TASK」と表示して欲しいというものでした.stepsのclass属性はDTD
 
<!ATTLIST steps class  CDATA "- topic/ol task/steps ">
 
と定義されています、つまり番号つきリストと同じだった訳です.今まではstepsは対応するテンプレートを作っていませんでしたのでtopic/olのテンプレートが動いていたはずです.最初は次のようにしました.
 
<!-- 既存のtopic/olのテンプレート
     name="topicOl"をつけてcall-templateで呼び出せるようにする.
 -->
<xsl:template match="*[contains(@class,' topic/ol ')]" name="topicOl">
    ....
</xsl:template>
 
<!-- 新規のtask/taskbody/stepsのテンプレート
     topic/olと競合するのでpriority="2"をつける
 -->
<xsl:template match="*[contains(@class,' task/steps ')]" priority="2">
    <!-- "TASK"と出力 -->
    <fo:block>
        <xsl:value-of select="'TASK'"/>
    </fo:block>
    <!-- このあとの処理はolと同じ -->
    <xsl:call-template name="topicOl"/>
</xsl:template>
 
ところが、必ずしもこれがベストではないことがあります.たとえばtopic/olのテンプレートが自分で自由にできない場合があります.他人が作ったテンプレートをカスタマイズするときはname="topicOl"と修正しづらいこともあります.他人がつくったものですからスタイルシートのバージョンが更新されればまた自分で直さねばなりません.
 
このようなときはxsl:next-matchが適役です.
 
<xsl:template match="*[contains(@class,' task/steps ')]" priority="2">
    <!-- "TASK"と出力 -->
    <fo:block>
        <xsl:value-of select="'TASK'"/>
    </fo:block>
    <!-- このあとはolと同じ -->
    <xsl:next-match/>
</xsl:template>
 
xsl:next-matchはpriorityによって選択されたスタイルシートの「次の候補の」テンプレートを呼び出してくれます.わざわざtopic/olのテンプレートに名前をつける必要はありません.
 
このxsl:next-matchは他のプログラミング言語の、継承元のクラスのメソッドを呼び出すのに似ていますね.もちろんXSLTにはクラスのような概念はありませんが、「次の候補の」テンプレートはたいていより共通性の高いテンプレートになりますから...
 
XSLT 2.0 Programmer's Referenceを見たら同じようなことがかいてありました.(以下原書から引用)
 
"There is a clear analogy here with object-oriented programming. Writing a template rule that overrides another is like writing a method that overrides a method defined on the super class. <xsl:next-match> behaves analogously to the super() function in object-oriented programming languages, allowing the new template rule to refine the behavior of the original template rule, rather than replacing it completely."
 
xsl:next-matchは特にDITAでは有効に使えそうですね.