XSLT2.0で便利になった機能(5) 正規表現

さて、見逃してならないXSLT2.0で便利になった機能に、正規表現(regular expression)が使えるようになったことがあります.とは言ってみたのですが、自分のスタイルシートを調べてみると、そう使っている箇所は多くありません.少しですが紹介してみたいと思います.
 
1.英語の索引のラベルを求める.
英語で索引を作るときは、いろいろな方法がありますが、例えばDocBookではindextermからテンポラリツリーを作ってソートし、結果を順に出力する方法があります.(バカ正直な方法です.)出力するときに、"A","B","C"...のように索引のグループラベルを作るのですが、このラベル文字を作るのに以下のようなコーディングをしています.
 
    <!-- indextermのキーの最初の文字 -->
    <xsl:variable name="firstChar" select="..."/>
    <xsl:choose>
        <!-- 最初の文字が大文字にマッチするか? -->
        <xsl:when test="matches(upper-case($firstChar),'\p{Lu}')">
            <xsl:value-of select="upper-case($firstChar)"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="'Symbol'"/>
        </xsl:otherwise>
    </xsl:choose>
 
ここで'\p{Lu}'は任意の大文字を表します.XSLT1.0ではmatchesもupper-caseもないので、仕方なく、
 
<xsl:when test="contains(translate($firstChar,'abcdefghijlmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ'),'ABCDEFGHIJKLMNOPQRSTUVWXYZ')">
 
と泥臭いコーディングをしなければならないでしょう.
 
2.属性の階層的処理
 
今作っているスタイルシートでは、スタイルを<xsl:attribute-set>で記述するのではなく、外部のファイルにXSLTライクな文法で定義する方法を取っています.例えばこんな具合です.
 
<attribute-set name="atsPaperBase">
    <attribute name="page-width">210mm</attribute>
    <attribute name="page-height">297mm</attribute>
</attribute-set>
<attribute-set name="atsCoverPage"  use-attribute-sets="atsPaperBase">
    <attribute name="margin-top">8mm</attribute>
    <attribute name="margin-right">0mm</attribute>
    <attribute name="margin-bottom">0mm</attribute>
    <attribute name="margin-left">24mm</attribute>
</attribute-set>
 
"atsCoverPage"はアトリビュートセット"atsPaperBase"を継承します.ここにはXSLTでも同じように複数のスタイルが書けます.例えば次の例は、"atsBase"は、"atsBaseIndent"、"atsBaseFontSize、atsBaseLineHeight"を継承しています.
 
<attribute-set name="atsBase"  use-attribute-sets="atsBaseIndent atsBaseFontSize atsBaseLineHeight"/>
 
これを処理するのに次のようにしています.ここでtmf:getAttributeSetは上記の定義をattribute()*として取得する関数です.
 
<xsl:function name="tmf:getAttributeSet" as="attribute()*">
    <xsl:param name="prmAttrSetName" as="xs:string"/>
    <xsl:for-each select="tokenize($prmAttrSetName, '[\s]+')">
        <xsl:variable name="attrSetName" select="string(.)"/>
        <!-- 名前が$attrSetNameのattribtesets/@use-attribute-sets
               指定された上位のスタイルを処理 -->
        <xsl:if test="$attrSetNameで指定されたattributesetにuse-attribute-setsあり">
           <xsl:sequence select="tmf:getAttributeSet(string($attrSetNameのattributeset/@use-attribute-sets))"/>
        <xsl:if>
        <!-- $attrSetNameで与えられたattributesetsの子要素のattributeをattribute()*として返します-->
        <xsl:sequence select="..."/>
    </xsl:for-each>
</xsl:function>
 
ここで使っている正規表現は"[\s]+"ですが、これは1個以上のwhite-spaceを表します.tokenizeは第2引数を区切り文字として、第1引数の文字列を分解した文字列をxs:string*のsequenceとして返します.
 
XSLT1.0では、正規表現はおろかtokenizeもありません.スペースを区切りに見立てて、contains(), substring-before(),substringafter()の組み合わせで再帰呼び出しによって処理してゆかねばならないでしょう.
 
このように、正規表現は非常に便利です.このほかにも、Unicodeのブロックを指定してマッチングができます.例えば、"\p{Hiragana}"はひらがなにマッチさせることができます.XSLT1.0では、文字が特定の範囲にあることを判定するようなこと
 
<xsl:if test="($char &gt;= 'あ') and ($char &lt;='ん')">
 
は、そもそもできませんでした.この機能があれば、スタイルシートの可能性は非常に広がると思います.例えば、多言語の文書でフォントを正確に割り当ててゆくことも可能ですね.せっかく使えるようになった正規表現、大いに利用したいものです.