xsl:numberについて紹介したいと思います.xsl:numberはXSLT2.0になっても大きな変更はありませんでした.select属性が加わったことだけの違いです.しかし、この違いは大きいです.従来は、カレントノードからしかカウントができませんでした.select属性を指定することにより、任意のノードからカウントができるようになりました.またこれは、カレントノードが存在しないxsl:functionの中でもxsl:numberが使えるようになったことを意味します.
例を考えてみましょう.例えば、テーブルのタイトル行に「表N-M 変換コード表」などと出力したいとします.ここでNは章の番号、Mは章内の連番です.このような時、部、章内のtable要素をカウントしなければなりません.
DocBookの場合でしたら文書はsetやbookをトップにした完全なツリー型なので、それほど問題はないと思われます.
xmlns:db="http://docbook.org/ns/docbook"
<xsl:template match="db:table">
<!-- chapterを数える-->
<xsl:variable name="chapterCount" select="string(count(ancestor::db:chapter/preceding-sibling::db:chapter) + 1)" as="xs:string"/>
<xsl:variable name="chapterCount" select="string(count(ancestor::db:chapter/preceding-sibling::db:chapter) + 1)" as="xs:string"/>
<!-- 自分のchapter内の位置を求める-->
<xsl:number select="."
level="any"
count="db:table"
from="db:chapter"/>
<xsl:number select="."
level="any"
count="db:table"
from="db:chapter"/>
</xsl:template>
しかし、DITAの場合中間ファイルが次のようなヤクザな形式をしているために、到底DocBookのようにはゆきません.DITAの文書はtopicと呼ばれる小規模なXML文書の集合で構成されます.これをDITA Open Toolkitで処理すると次のような中間ファイルが出来上がります.文書の構成は前半のmap部分で定義され、個々の内容は後半のtopic部分に格納されます.
<!-- DITAの中間ファイル:模式的な図-->
<dita-merge>
<bookmap>
...
<!-- map部:partもchapterもすべてtopicrefで表される-->
<topicref href="#unique_1" ..../>
<topicref href="#unique_1" ..../>
...
</bookmap>
<topic id="unique_1">
<!-- 実際のtopicの内容-->
</topic>
<topic id="unique_2">
...
</topic>
</dita-merge>
<dita-merge>
<bookmap>
...
<!-- map部:partもchapterもすべてtopicrefで表される-->
<topicref href="#unique_1" ..../>
<topicref href="#unique_1" ..../>
...
</bookmap>
<topic id="unique_1">
<!-- 実際のtopicの内容-->
</topic>
<topic id="unique_2">
...
</topic>
</dita-merge>
DITAで章内のテーブルのカウントを求めるには以下のようにします.
テーブルの章内のカウント= そのtopicまでのカウント + そのtopic内のカウント
そのtopicまでのカウントは、あらかじめカウントしてテンポラリツリーに結果を格納しておきます.以下は関数でxsl:numberを使う例として御覧ください.
xmlns:tmf="http://acme.com/tmakita"
<!-- dita-merge要素 -->
<xsl:variable name="top" select="/*[1]" as="element()"/>
<xsl:variable name="top" select="/*[1]" as="element()"/>
<xsl:function name="tmf:getTableCount" as="xs:string">
<xsl:param name="prmTopicRef" as="element()"/>
<xsl:param name="prmTable" as="element()"/>
<xsl:param name="prmTopicRef" as="element()"/>
<xsl:param name="prmTable" as="element()"/>
<xsl:variable name="tablePreviousAmount" as="xs:integer">
<!--ここはあらかじめカウントしてある結果から、tableが含まれる
topicまでの集計値を得る.
-->
</xsl:variable>
<!--ここはあらかじめカウントしてある結果から、tableが含まれる
topicまでの集計値を得る.
-->
</xsl:variable>
<xsl:variable name="tableCurrentAmount" as="xs:integer">
<xsl:number select="$prmTable"
level="any"
count="*[contains(@class,' topic/table ')]"
from="*[contains(@class, ' topic/topic ')][parent::* is $top]"/>
</xsl:variable>
<xsl:number select="$prmTable"
level="any"
count="*[contains(@class,' topic/table ')]"
from="*[contains(@class, ' topic/topic ')][parent::* is $top]"/>
</xsl:variable>
<xsl:variable name="tableNumber" select="$tablePreviousAmount + $tableCurrentAmount" as="xs:integer"/>
<xsl:sequence select="string($tableNumber)"/>
</xsl:function>
</xsl:function>
xsl:numberの使用例を見ていると、xsl:numberはtext()を返すので、直接結果ツリーに反映させるようなものが多いです.しかしXSLT2.0では、text()は容易にxs:stringやxs:integrにキャストできますので、もっとxsl:numberは活用できるのではないかと思います.