DITAのindextermをソートする

<indexterm>の話です.DITAのindextermはDocBookなんかと違って、無限(?)にネストできます.例えば
 
<indexterm>www
  <indexterm>w3
    <indexterm>org
    </indexterm>
  </indexterm>
</indexterm>
 
というような具合です.それではこれをソートするのにどうすればいいのでしょうか?今までいろいろ試行錯誤したのですが、たとえXSLT 2.0であっても単純な<xsl:sort/>ではできないことだけはわかりました.何故かと言うと、「ソートキーの個数が可変」だからです.
以前は、上の例だと、概念的に
 
[www][w3][org]
 
という3つの部分からなる固定長の文字列を作っていました.固定長というのはindextermのテキストの後ろにスペースコードをパディングして各部を一定の文字数にしたものです.これをindextermの入れ子分だけ結合して一つの文字列にしてソートキーにしていました.見かけは動くのですが、実は<xsl:sort>ではパディングしたスペースキーは何の役にも立たないことがわかりました.キーとしては読み飛ばしてしまうようです.やっぱりJavaで言えばStringじゃなくてString []をキーにしてソートできなければダメです.
 
そこで何か手はないものかとWEBを探したのですが、やはり世の中には同じ問題にぶつかっている人はいるもので、相当前のxsl-listに次のようなサンプルコードつきの投稿がありました.sourceforgeのFXSLの作者Dimitre Novatchevのものです.
 
[xsl] Sorting with unknown number of sort keys (Was: Re: Sort question)
http://www.biglist.com/lists/xsl-list/archives/200303/msg00007.html
 
コードは古くてXSLT1.0の時代のものです.でも中を読んでみて衝撃をうけました.「こりゃいけるんじゃないかい?」
私はXSLT1.0といえばコケにするのが常でしたが、このコードは違いました.XSLTの深淵を垣間見た感じです.やっぱり出来る人は違いますね.試しにSaxon9.3で動かしてみたのですが、なんと2ステップ直しただけで完璧に動きました.スゴイ!
 
以下にテンポラリツリーをソートした例を示します.実際のindextermとは違いますが、可変個数の<key>を使ってソートすると考えてください.
 
<xsl:template match="/">
 <xsl:variable name="input">
  <indexterm>
   <key>らいせんすかんりせんようPC</key>
  </indexterm>
  <indexterm>
   <key>らいせんすかんりそふとうぇあ</key>
  </indexterm>
  <indexterm>
   <key>らいせんす</key>
  </indexterm>
  <indexterm>
   <key>らいせんす</key>
   <key>すたんどあろんらいせんす</key>
  </indexterm>
  <indexterm>
   <key>らいせんす</key>
   <key>さーばーらいせんす</key>
  </indexterm>
  <indexterm>
   <key>らいせんす</key>
   <key>でべろぱーらいせんす</key>
  </indexterm>
  <indexterm>
   <key>らいせんす</key>
   <key>でべろぱーらいせんす</key>
   <key>Visual C++よう</key>
  </indexterm>
  <indexterm>
   <key>らいせんす</key>
   <key>でべろぱーらいせんす</key>
   <key>Javaよう</key>
  </indexterm>
  <indexterm>
   <key>らいせんすわりつけをへんこうする</key>
  </indexterm>
 </xsl:variable>
 <output>
  <!--ここからサンプルのコードを呼び出す-->
  <xsl:call-template name="indexSort">
   <xsl:with-param name="prmIndexterm" select="$input"/>
  </xsl:call-template>
 </output>
</xsl:template>
 
<xsl:template name="indexSort">
 <xsl:param name="prmIndexterm" as="document-node()"/>
 <xsl:call-template name="hSort">
  <xsl:with-param name="pList" select="$prmIndexterm/*"/>
  <xsl:with-param name="pFunGT"
  select="document('')/*/myFunGT:*[1]"/>
 </xsl:call-template>
</xsl:template>
 
出力はこんな感じです.
 
<output>
 <indexterm>
  <key>らいせんす</key>
 </indexterm>
 <indexterm>
  <key>らいせんす</key>
  <key>さーばーらいせんす</key>
 </indexterm>
 <indexterm>
  <key>らいせんす</key>
  <key>すたんどあろんらいせんす</key>
 </indexterm>
 <indexterm>
  <key>らいせんす</key>
  <key>でべろぱーらいせんす</key>
 </indexterm>
 <indexterm>
  <key>らいせんす</key>
  <key>でべろぱーらいせんす</key>
  <key>Javaよう</key>
 </indexterm>
 <indexterm>
  <key>らいせんす</key>
  <key>でべろぱーらいせんす</key>
  <key>Visual C++よう</key>
 </indexterm>
 <indexterm>
  <key>らいせんすかんりせんようPC</key>
 </indexterm>
 <indexterm>
  <key>らいせんすかんりそふとうぇあ</key>
 </indexterm>
 <indexterm>
  <key>らいせんすわりつけをへんこうする</key>
 </indexterm>
</output>
 
問題は文字列の比較です.このスタイルシートでは文字列比較を"node-strComp"というテンプレートでxsl:sortでやっています.これだとうまくいきません.単純なコード順になってしまうからです.例えば"C"と"c"はUnicodeのコード順では"C"<"c"ですが、索引では"c"<"C"で"c"の方が先に現れてくれないと困ります.でもこれはxsl:sortにlangを指定してやればうまくゆくでしょう.例えば<xsl:sort lang="en"/>という感じです.
パフォーマンスからすれば、外部のJavaライブラリを使ってソートしたほうがたぶん早いのでしょうが、ともかくXSLTスタイルシートで拡張ライブラリなしで可変個数のキーのソートができてしまうのはすごいです.やはり先人の資産には学ばねばなりません.