次にXSLT2.0で便利になったなと思わせるのは、やはり<xsl:function>が導入され、XPathの中から<xsl:function>で定義した関数が簡単に呼び出せるようになったことではないかと思います.さて思い返せばXSLT1.0では、たぶん次のようなことをやっていたはずです.
スタイルシートが複雑になってくると、テンプレートの中で条件判断するのに、<xsl:if test="...">や<xsl:choose><xsl:when test="....">に記述できるほど条件が簡単ではなくなってくる.
しかたがないのでテンプレート中に、<xsl:variable>で判定の手助けをするようなローカル変数をつくってやる.例えば前と同じ例で恐縮ですが、
<xsl:variable name="lang">
<xsl:choose>
<xsl:when test="@xml:lang">
<xsl:value-of select="@xml:lang"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'en'"/>
</xsl:othewise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="@xml:lang">
<xsl:value-of select="@xml:lang"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'en'"/>
</xsl:othewise>
</xsl:choose>
</xsl:variable>
そして、これを
<xsl:if test="$lang='ja'">
<!-- "ja"固有の処理 -->
...
</xsl:if>
<!-- "ja"固有の処理 -->
...
</xsl:if>
と判定に使います.ところで、同じ処理をあちこちでやると、共通化が必要になり、これをテンプレート化して、次のような使い方をします.
<xsl:variable name="lang>
<xsl:call-template name="getLang">
<xsl:with-param name="prmCurrent" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="getLang">
<xsl:with-param name="prmCurrent" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:template name="getLang">
<xsl:param name="prmCurrent">
<xsl:choose>
<xsl:when test="$prmCurrent/@xml:lang">
<xsl:value-of select="$prmCurrent/@xml:lang"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'en'"/>
</xsl:othewise>
</xsl:choose>
</xsl:template>
<xsl:param name="prmCurrent">
<xsl:choose>
<xsl:when test="$prmCurrent/@xml:lang">
<xsl:value-of select="$prmCurrent/@xml:lang"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'en'"/>
</xsl:othewise>
</xsl:choose>
</xsl:template>
テンプレート"getLang"は、この手の種類の場合テキストしか返す手段がありませんでした.もちろん、RTF(Result Tree Fragment)と呼ばれる一時ツリーを返しても良いのですが、それでは呼び出し元で使いようがなかったからです.
XSLT2.0では、このような問題を<xsl:function>でスマートに解決してくれます.上の例を<xsl:function>で書き直すとどうなるでしょうか?まず、関数の名前のネームスペースを定義します.
<xsl:function name="tmf:getlang" as="xs:string">
<xsl:param name="prmCurrent" as="element()">
<xsl:choose>
<xsl:when test="$prmCurrent/@xml:lang">
<xsl:sequence select="string($prmCurrent/@xml:lang)"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="'en'"/>
</xsl:othewise>
</xsl:choose>
</xsl:function>
<xsl:param name="prmCurrent" as="element()">
<xsl:choose>
<xsl:when test="$prmCurrent/@xml:lang">
<xsl:sequence select="string($prmCurrent/@xml:lang)"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="'en'"/>
</xsl:othewise>
</xsl:choose>
</xsl:function>
なんだか<xsl:variable>とそう変わりませんね.問題は呼び出し方です.以下のようになります.
<xsl:if test="tmf:getLang(.)='ja'">
<!-- "ja"固有の処理 -->
...
</xsl:if>
<!-- "ja"固有の処理 -->
...
</xsl:if>
testに記述する「XPath式のなか」から呼び出せてしまいます.わざわざ
<xsl:variable name="lang" as="xs:string" select="tmf:getLang(.)"/>
などと書く必要はありません.関数は非常に便利です.私も今までどれだけXSLT1.0のテンプレートを関数に書き直してきたかわかりません.
例えば上記の例は戻り値がxs:stringでしたが、XSLT1.0で以下のようなコーディングやった経験のあるかたはおられませんか?
<xsl:variable name="true" select="'true'"/>
<xsl:variable name="false" select="'false'"/>
<xsl:variable name="false" select="'false'"/>
<xsl:variable name="isEn>
<xsl:call-template name="isEn">
<xsl:param name="prmCurrent" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="isEn">
<xsl:param name="prmCurrent" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:template name="isEn">
<xsl:param name="prmCurrent">
<xsl:choose>
<xsl:when test="$prmCurrent/@xml:lang">
<xsl:choose>
<xsl:when test="$prmCurrent/@xml:lang='en'">
<xsl:value-of select="$true"/>
<xsl:when>
<xsl:otherwise>
<xsl:value-of select="$false"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<!-- default language="en" -->
<xsl:value-of select="$true"/>
</xsl:othewise>
</xsl:choose>
</xsl:template>
<xsl:param name="prmCurrent">
<xsl:choose>
<xsl:when test="$prmCurrent/@xml:lang">
<xsl:choose>
<xsl:when test="$prmCurrent/@xml:lang='en'">
<xsl:value-of select="$true"/>
<xsl:when>
<xsl:otherwise>
<xsl:value-of select="$false"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<!-- default language="en" -->
<xsl:value-of select="$true"/>
</xsl:othewise>
</xsl:choose>
</xsl:template>
そして、これを
<xsl:if test="$isEn=$true">
<!-- "en"固有の処理 -->
...
</xsl:if>
<!-- "en"固有の処理 -->
...
</xsl:if>
と使います.関数的に使いたいテンプレートからは事実上テキストしか返せなかったのでこうならざるを得ないのです.この例も<xsl:function>ではスマートになります.
<xsl:function name="tmf:isEn" as="xs:boolean">
<xsl:param name="prmCurrent">
<xsl:choose>
<xsl:when test="$prmCurrent/@xml:lang">
<xsl:choose>
<xsl:when test="$prmCurrent/@xml:lang='en'">
<xsl:sequence select="true"/>
<xsl:when>
<xsl:otherwise>
<xsl:sequence select="false"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<!-- default language="en" -->
<xsl:sequence select="true"/>
</xsl:othewise>
</xsl:choose>
</xsl:function>
<xsl:param name="prmCurrent">
<xsl:choose>
<xsl:when test="$prmCurrent/@xml:lang">
<xsl:choose>
<xsl:when test="$prmCurrent/@xml:lang='en'">
<xsl:sequence select="true"/>
<xsl:when>
<xsl:otherwise>
<xsl:sequence select="false"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<!-- default language="en" -->
<xsl:sequence select="true"/>
</xsl:othewise>
</xsl:choose>
</xsl:function>
呼び出し側は
<xsl:if test="tmf:isEn(.)">
<!-- "en"固有の処理 -->
...
</xsl:if>
<!-- "en"固有の処理 -->
...
</xsl:if>
の一発の関数呼び出しで済みます.
関数は、便利なのですが以前述べたtunnelパラメータは<xsl:function>を通過してくれません.また宣言したパラメータは必須になります.<xsl:param>の記述にrequired="yes"とかselect="xxx"と書くとコンパイル時にXSLTプロセッサに怒られます.こんなところが注意事項でしょうか?
ともかく、関数を自由自在に使いこなせれば、スタイルシートの記述もとても楽になります.ぜひお試しください.