XSLT 2.0で便利になった機能(28) xsl:next-matchを使う

xsl:next-matchについて紹介します.xs:next-matchは、複数のマッチングするテンプレートがあった場合に、最終的に選択されたテンプレート内から、次の優先順位でマッチングするテンプレートを階層的に呼び出せる機能です.
 
例えばDITA→XSL-FOでxsl:next-matchは次のように使うことができます.
 
<xsl:template match="*[contains(@class,' topic/ph ')]" priority="1">
    <xsl:param name="prmAttNode" as="attribute()*" required="no" select="()"/>
    <fo:inline>
        <xsl:copy-of select="$prmAttNode"/>
        <xsl:apply-templates/>
    </fo:inline>
</xsl:template>
<xsl:template match="*[contains(@class,' hi-d/b ')]" priority="2">
    <xsl:next-match>
        <xsl:with-param name="prmAttNode" select="$attB"/>
    </xsl:next-match>
</xsl:template>
<xsl:template match="*[contains(@class,' hi-d/i ')]" priority="2">
    <xsl:next-match>
        <xsl:with-param name="prmAttNode" select="$attI"/>
    </xsl:next-match>
</xsl:template>
<xsl:template match="*[contains(@class,' hi-d/u ')]" priority="2">
    <xsl:next-match>
        <xsl:with-param name="prmAttNode" select="$attU"/>
    </xsl:next-match>
</xsl:template>
<xsl:template match="*[contains(@class,' hi-d/tt ')]" priority="2">
    <xsl:next-match>
        <xsl:with-param name="prmAttNode" select="$attTt"/>
    </xsl:next-match>
</xsl:template>
<xsl:template match="*[contains(@class,' hi-d/sup ')]" priority="2">
    <xsl:next-match>
        <xsl:with-param name="prmAttNode" select="$attSup"/>
    </xsl:next-match>
</xsl:template>
<xsl:template match="*[contains(@class,' hi-d/sub ')]" priority="2">
    <xsl:next-match>
        <xsl:with-param name="prmAttNode" select="$attSub"/>
    </xsl:next-match>
</xsl:template>

<xsl:variable name="attB" as="attribute()*">
    <xsl:attribute name="font-weight" select="'bold'"/>
</xsl:variable>
<xsl:variable name="attI" as="attribute()*">
    <xsl:attribute name="font-style" select="'italic'"/>
</xsl:variable>
<xsl:variable name="attU" as="attribute()*">
    <xsl:attribute name="text-decoration" select="'underline'"/>
</xsl:variable>
<xsl:variable name="attTt" as="attribute()*">
    <xsl:attribute name="font-family" select="'Courier New'"/>
    <xsl:attribute name="font-size" select="'0.9em'"/>
</xsl:variable>
<xsl:variable name="attSup" as="attribute()*">
    <xsl:attribute name="font-size" select="'0.8em'"/>
    <xsl:attribute name="baseline-shift" select="'super'"/>
</xsl:variable>
<xsl:variable name="attSub" as="attribute()*">
    <xsl:attribute name="font-size" select="'0.8em'"/>
    <xsl:attribute name="baseline-shift" select="'sub'"/>
</xsl:variable>
<xsl:template match="p">
    <fo:block>
        <xsl:apply-templates/>
    </fo:block>
</xsl:template>
 
これに
 
    <p>
        <b>Bold</b>
        <i>Italic</i>
        <u>Underline</u>
        <tt>Teletype</tt>
        <sup>Superscript</sup>
        <sub>Subscript</sub>
    </p>
 
のようなデータを入力すれば、
 
    <fo:block>
        <fo:inline font-weight="bold">Bold</fo:inline>
        <fo:inline font-style="italic">Italic</fo:inline>
        <fo:inline text-decoration="underline">Underline</fo:inline>
        <fo:inline font-family="Courier New" font-size="0.9em">Teletype</fo:inline>
        <fo:inline font-size="0.8em" baseline-shift="super">Superscript</fo:inline>
        <fo:inline font-size="0.8em" baseline-shift="sub">Subscript</fo:inline>
    </fo:block>
 
という出力を得ることができます.xsl:next-matchが機能しているのがお分かりになりますでしょうか?
 
このようにな秘密はDITAのDTDにあります.DITAのテンプレートでは、match属性に、*[contans(@class, ' xxx/yyy ')]と書くのがしきたりですが、これはDTDのclass属性が次のように割り当てられているためです.DITAの中間ファイルが作られるとき、パーサーによりこのclass属性の値がつけられます.
 
<!ATTLIST ph          %global-atts;  class CDATA "- topic/ph "         >
<!ATTLIST b           %global-atts;  class CDATA "+ topic/ph hi-d/b "  >
<!ATTLIST i           %global-atts;  class CDATA "+ topic/ph hi-d/i "  >
<!ATTLIST sub         %global-atts;  class CDATA "+ topic/ph hi-d/sub ">
<!ATTLIST sup         %global-atts;  class CDATA "+ topic/ph hi-d/sup ">
<!ATTLIST tt          %global-atts;  class CDATA "+ topic/ph hi-d/tt " >
<!ATTLIST u           %global-atts;  class CDATA "+ topic/ph hi-d/u "  >

class属性からわかるとおり、b、i、sub、sup、tt、uは、phから派生した要素と考えることができます.上記のスタイルシートは、言ってみればオブジェクトオリエンテッドプログラミング(OOP)で、派生したクラスのメソッドから、super()を呼ぶのにも似ています.
 
しかし、XSLTはxsl:next-matchを使うときは、注意が必要です.次の優先順位でマッチングするテンプレートは、XSLTプロセッサの判断に委ねられます.priority属性を使って、意図どおりのテンプレートに次の優先順位が決まるようにするなど、コーディングをする方は気をつけなければならないでしょう.