文書系のスタイルシートを作っていると、なかなかxsl:sortにお目にかかる機会がありません.せいぜいあるとすれば<indexterm>をABC順(日本語ならあいうえお順)にソートするときくらいでしょうか?今回たまたまxsl:sortを使う機会がありました.どのような場合かというと、次のようなDITAのsimpletableを空欄を先に縦結合ついで横結合するというものです.
でもやたらに空欄を縦結合するわけにはゆかないのであくまでも2列目以降の縦結合は、1列目を縦結合した範囲で行うものとします.
そうするとまず最初に1列目から最終列に向かって縦結合を生成しなければなりません.これを行った結果は従来の行/列の順のセルの順が崩れて、列/行の順になります.例えば次のようなstentryのデータになるでしょう.ここでrow-ends-hereは列の最後を示すマーカーです.
<stentry row-no="1" col-no="1" morerows="4">1</stentry>
<stentry row-no="6" col-no="1" morerows="1">2</stentry>
<stentry row-no="1" col-no="2" morerows="1">Item 1</stentry>
<stentry row-no="3" col-no="2" morerows="1">Item 2</stentry>
<stentry row-no="5" col-no="2">Item 3</stentry>
<stentry row-no="6" col-no="2">Item 1</stentry>
<stentry row-no="7" col-no="2">Item 2</stentry>
<stentry row-no="1" col-no="3">A</stentry>
<stentry row-no="2" col-no="3">B</stentry>
<stentry row-no="3" col-no="3">C</stentry>
<stentry row-no="4" col-no="3">D</stentry>
<stentry row-no="5" col-no="3">E</stentry>
<stentry row-no="6" col-no="3">E</stentry>
<stentry row-no="7" col-no="3">F</stentry>
<stentry row-no="1" col-no="4" morerows="1">That and this</stentry>
<stentry row-no="3" col-no="4" morerows="1">What is this?</stentry>
<stentry row-no="5" col-no="4">Many things</stentry>
<stentry row-no="6" col-no="4" morerows="1">Exists!</stentry>
<stentry row-no="1" col-no="5" morerows="3">-</stentry>
<row-ends-here row-no="1" col-no="9999"/>
<row-ends-here row-no="2" col-no="9999"/>
<row-ends-here row-no="3" col-no="9999"/>
<row-ends-here row-no="4" col-no="9999"/>
<stentry row-no="5" col-no="5">-</stentry>
<row-ends-here row-no="5" col-no="9999"/>
<stentry row-no="6" col-no="5" morerows="1">-</stentry>
<row-ends-here row-no="6" col-no="9999"/>
<row-ends-here row-no="7" col-no="9999"/>
これはうまく縦結合されていますが、このままではXSL-FOに変換できません.列/行を行/列の順に戻してやらなければならないのです.このようなときに使えるのがxsl:perform-sortです.xsl:perform-sortは、@selectでシーケンスを選択し、子要素のxsl-sortでソートキーを指定します.ですので、次のように書くことができます.
<xsl:perform-sort select="[stentryとrow-ends-hereのシーケンス]">
<xsl:sort select="@row-no" data-type="number"/>
<xsl:sort select="@col-no" data-type="number"/>
</xsl:perform-sort>
xsl:sortはXSLT1.0ではxsl:for-eachとxsl:apply-templatesの子要素としか書けませんでした.XSLT2.0では対象を明確に@selectで指定して単純にシーケンスのソート結果を得ることができます.
ソートした結果を<xsl:for-each-group group-ending-with="row-ends-here">でグルーピングしてstrowを生成してやれば次のような結果をえることができます.
<strow row-no="1">
<stentry row-no="1" col-no="1" morerows="4">1</stentry>
<stentry row-no="1" col-no="2" morerows="1">Item 1</stentry>
<stentry row-no="1" col-no="3">A</stentry>
<stentry row-no="1" col-no="4" morerows="1">That and this</stentry>
<stentry row-no="1" col-no="5">-</stentry>
</strow>
<strow row-no="2">
<stentry row-no="2" col-no="3">B</stentry>
</strow>
<strow row-no="3">
<stentry row-no="3" col-no="2" morerows="1">Item 2</stentry>
<stentry row-no="3" col-no="3" >C</stentry>
<stentry row-no="3" col-no="4" morerows="1">What is this?</stentry>
</strow>
<strow row-no="4">
<stentry row-no="4" col-no="3">D</stentry>
</strow>
<strow row-no="5">
<stentry row-no="5" col-no="2">Item 3</stentry>
<stentry row-no="5" col-no="3">E</stentry>
<stentry row-no="5" col-no="4">Many things</stentry>
<stentry row-no="5" col-no="5">-</stentry>
</strow>
<strow row-no="6">
<stentry row-no="6" col-no="1" morerows="1">2</stentry>
<stentry row-no="6" col-no="2">Item 1</stentry>
<stentry row-no="6" col-no="3">E</stentry>
<stentry row-no="6" col-no="4" morerows="1">Exists!</stentry>
<stentry row-no="6" col-no="5" morerows="1">-</stentry>
</strow>
<strow row-no="7">
<stentry row-no="7" col-no="2">Item 2</stentry>
<stentry row-no="7" col-no="3">F</stentry>
</strow>
XSL-FOへレイアウトした結果は以下のようになります.本当のDITAにはstenrtyは縦結合/横結合はないのでそのロジックは加えてやらねばなりません.(この例では横結合はありません.)
ソーティングとグルーピング、それとテンポラリツリーをうまく使うとXSLT1.0では考えることができなかったマジックがXSLT2.0では可能です.この表の自動スパンはその良い例でしょう.