XSLT3.0への道(8) パターン (その1)

パターンとはxsl:templateのmatch属性に書く条件のことです.XSLT3.0ではこのパターンがグンと拡張されます.どれくらい変わったかというと定義をみれば歴然とします.次が今のXSLT2.0でのパターンの定義です.きわめてシンプルでした.
 
[1] Pattern ::= PathPattern
         | Pattern '|' PathPattern
[2] PathPattern ::= RelativePathPattern
                   | '/' RelativePathPattern?
                   | '//' RelativePathPattern
                   | IdKeyPattern (('/' | '//') RelativePathPattern)?
 
XSLT3.0だとこうなります.
 
[1] Pattern ::= PatternTerm ( ('|' | 'union') PatternTerm )*
[2] PatternTerm ::= BasicPattern ( ('intersect' | 'except') BasicPattern )*
[3] BasicPattern ::= TypePattern | PathPattern | QualifiedPattern
[4] QualifiedPattern ::= '(' Pattern ')' PredicateList
[5] TypePattern ::= '~' ItemType PredicateList
[6] PathPattern ::= RelativePathPattern
                   | '/' RelativePathPattern?
                   | '//' RelativePathPattern
                   | RootedPattern
違いは、
1. intersectやexceptがパターンにも書けるようになった.
2. 従来パターンはPathPatternだけだったが、TypePatternが新たに導入された.
3. Patternを"()"で囲んで次に制約条件を"[]"で書けるQualifiedPatternが新設された.
4. PathPatternにRootedPatternが追加された.これにより例えば$a/paraなどと書けるようになった.
一番大きい変更は2.ではないかと思います.従来パターンは次のようにパスと条件を書いていました.これがPathPatternです.
 
<xsl:template match="para[@xml:lang eq 'ja']">
 
ところがXSLT3.0では新しいTypePatternでmatchの対象に任意のAtomic valueや関数が書けてしまいます.TypePatternは先頭に"~"(チルダ)をつけてPathPatternと区別します.以下はドラフトに載っている例です.
 
<xsl:template match="~xs:integer[. mod 2 = 0]">
 
この場合、2の倍数の整数にマッチするテンプレートとなります.ややこしいのですが、node()もAtomic valueの一つなので、
 
<xsl:template match="~node()">
 
とも書けてしまいます.この場合任意のノードにマッチします.難解なのが関数です.
 
<xsl:template match="~function(xs:integer) as xs:integer">
 
はxs:integerを引数に取り、xs:integerを戻り値とする関数にマッチします.
 
最初の整数の例の場合は、例えば次のようにコードを書けば動かすことが出来るように思います.
 
<xsl:variable name="intSeqence" as="xs:integer*" select="(1,2,3,4,5)">
<xsl:apply-templates select="$intSequence">
 
こうすれば偶数の2、4だけがマッチしてテンプレートが起動されるでしょう.
 
しかし関数の場合はどのような使うのでしょう?パターンに記述できるということは逆にxsl:apply-templateの方に関数の指定があってもよい様に思えるのですがサンプルは載っていませんでした.今後詳しく解説されるのでしょう.
 
あとxs:integerなどのAtomic valueをパターンに追加して、どんな御利益があるのかと疑問に思う方もいらっしゃるかもしれません.確かにそのとおりなのですが、ドラフトのxsl:apply-templatesのところに次のような例が載っています.
 
<xsl:template name="main">
  <xsl:apply-templates select="unparsed-text-lines('input.txt')"/>
</xsl:template>
<xsl:template match="~xs:string[starts-with(., '==')]">
  <h2><xsl:value-of select="replace(., '==', '')"/></h2>
</xsl:template>
<xsl:template match="~xs:string[starts-with(., '::')]">
  <p class="indent"><xsl:value-of select="replace(., '::', '')"/></p>
</xsl:template>
<xsl:template match="~xs:string">
  <p class="body"><xsl:value-of select="."/></p>
</xsl:template>
 
unparsed-text-lines()はXPath3.0で新たに追加された関数で、引数のテキストファイルの内容を行ごとにxs:stringで返してくれる便利な関数です.
つまり、<xsl:apply-templates select="unparsed-text-lines('input.txt')"/>で、input.txtの行ごとに「apply-templateされる」ことになります.
二番目のテンプレートは最初が"=="である行にマッチします."=="を<h2>に変換するのはおなじみのWiki文法です.(三番目のテンプレートの"::"で始まる行は段落に変換されていますが、"::"はWiki文法には見当たりません.)
 
このように、簡単なテキスト変換だったらmatch="~xs:string[条件]"で書けてしまうのがわかります.[条件]のところにmatches()関数を使って正規表現を書けばもっときめ細かいマッチングができるでしょう.
この例から見てパターンの拡張の本命は実は~xs:stringによるテキスト処理に思えます.XSLTは主入力はあくまでXMLですが、テキストファイルもこの例のように変換ができてしまうレベルになりました.XSLT1.0の頃では思いもよらなかった拡張です.