最近他の方の書いたスタイルシートを見ることがあるのですが意外とposition()関数を使っていないようです.ご存知の方にとってみればなんでもないのですが、便利な使い方があるので紹介します.
例えば次のようなデータ構造があったとします.
<div>
<span>りんご</span>
<span>みかん</span>
<span>バナナ</span>
</div>
<span>りんご</span>
<span>みかん</span>
<span>バナナ</span>
</div>
<xsl:template match="div">
<fo:block>
<xsl:apply-templates select="span"/>
</fo:block>
</xsl:template>
<fo:block>
<xsl:apply-templates select="span"/>
</fo:block>
</xsl:template>
で、次のような出力を得たいとします.
<fo:block>【りんご,みかん,バナナ】</fo:block>
私がお目にかかるテンプレートはほとんどの場合以下のようなものです.
<xsl:template match="span">
<xsl:if test="not(preceding-sibling::span)">
<xsl:value-of select="'【'"/>
</xsl:if>
<xsl:if test="preceding-sibling::span">
<xsl:value-of select="','"/>
</xsl:if>
<xsl:apply-templates/>
<xsl:if test="not(following-sibling::span)">
<xsl:value-of select="'】'"/>
</xsl:if>
</xsl:template>
<xsl:if test="not(preceding-sibling::span)">
<xsl:value-of select="'【'"/>
</xsl:if>
<xsl:if test="preceding-sibling::span">
<xsl:value-of select="','"/>
</xsl:if>
<xsl:apply-templates/>
<xsl:if test="not(following-sibling::span)">
<xsl:value-of select="'】'"/>
</xsl:if>
</xsl:template>
で、私はこのようにXpathでノード条件を書くのがあまり好きではありません.ではどのように書くかといいますと、
<xsl:template match="span">
<xsl:choose>
<xsl:when test="position() eq 1">
<xsl:value-of select="'【'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="','"/>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates/>
<xsl:if test="position() eq last()">
<xsl:value-of select="'】'"/>
</xsl:if>
</xsl:template>
<xsl:choose>
<xsl:when test="position() eq 1">
<xsl:value-of select="'【'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="','"/>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates/>
<xsl:if test="position() eq last()">
<xsl:value-of select="'】'"/>
</xsl:if>
</xsl:template>
とします.いかがでしょう?スマートではないでしょうか?もしかしたらエッ!そんなこと出来るの?と思われたかもしれません.ハイできます.
position()関数は、"context position"を返します."context position"というのは、この場合処理しているのがspan要素なので、current nodeの位置です.では何の中の位置なのかといいますと、"within the sequence of items currently being processed"の中の位置なのです.
もし興味のある方がおられましたら、XPathの仕様をご覧ください.
2.1.2 Dynamic Context
http://www.w3.org/TR/xpath20/#eval_context
http://www.w3.org/TR/xpath20/#eval_context
実は、div要素のテンプレートで、<xsl:apply-templates select="span"/>とやっていますが、select="span"で対象となるdiv要素のシーケンス(element()*)がXSLTプロセッサによって作られます.position()はこのシーケンスの中の位置なのです.あらかじめspanの処理が始まる前にこのシーケンスが作られます.なので"position() eq last()"という条件も対象のspan要素のシーケンスの中の最後の位置になっているかを判定しているだけなのです.
あとposition()を使っていても、"position() > 1"と書いておられる方もいます.歯を食いしばってこのように書く必要はぜんぜんありません."position() gt 1"でなんら問題ないのです.
もしご存じなかったらposition()をぜひご活用ください.とても便利です.