さて、見逃してならないXSLT2.0で便利になった機能に、正規表現(regular expression)が使えるようになったことがあります.とは言ってみたのですが、自分のスタイルシートを調べてみると、そう使っている箇所は多くありません.少しですが紹介してみたいと思います.
1.英語の索引のラベルを求める.
英語で索引を作るときは、いろいろな方法がありますが、例えばDocBookではindextermからテンポラリツリーを作ってソートし、結果を順に出力する方法があります.(バカ正直な方法です.)出力するときに、"A","B","C"...のように索引のグループラベルを作るのですが、このラベル文字を作るのに以下のようなコーディングをしています.
<!-- indextermのキーの最初の文字 -->
<xsl:variable name="firstChar" select="..."/>
<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>
<!-- 最初の文字が大文字にマッチするか? -->
<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.属性の階層的処理
<attribute-set name="atsPaperBase">
<attribute name="page-width">210mm</attribute>
<attribute name="page-height">297mm</attribute>
</attribute-set>
<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>
<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>
<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 >= 'あ') and ($char <='ん')">