XSLT3.0への道(36) 変わるDITAのテンプレート(その2)

この前XPath 3.1で使えるようになったcontains-tokenを使ったDITAのマッチングテンプレートを紹介させていただきました.でもDITAのスタイルシートを作っていると、いろいろな@class属性にマッチするか、テストしたい場合が出てきます.例えばfigのテンプレートで、下位にtitleかdesc要素が存在するか調べたいとします.これは実際意味があって、DITAのfigをHTML5のfigureに変換するときに、titleかdescが有ったらfigcaptionという要素を生成してやる必要があったからです.contains-tokenを使えば次のようになるでしょう.

<xsl:function name="ahf:hasFigTitle" as="xs:boolean">
<xsl:param name="prmFig" as="element()"/>
<xsl:sequence select="$prmFig/*[contains-token(@class, 'topic/title') and contains-token(@class,'topic/desc')] => exists()"/>
</xsl:function>

 確かにそのとおりこれで動いてくれるのではありますが、あんまりおもしろくありません.contains-tokenを単に2つ記述してandで結んでいるだけだからです.こういうのを私の住んでいる長野県のお年寄りの言葉では「げーもねぇ」(あまりよろしくない状態を指す)と言います.

では、欲しいのはどんなものでしょうか?簡単に言えば、'topic/title'か'topic/desc'が含まれることを一発でチェックできる関数がほしいです.でもこれって考えてみるとすごく簡単なのです.私がやってみた実装は以下のとおりです.

<xsl:function name="ahf:seqContainsToken" as="xs:boolean">
  <xsl:param name="prmDstStr" as="xs:string*"/>
  <xsl:param name="prmTokens" as="xs:string*"/>
  <xsl:choose>
    <xsl:when test="empty($prmDstStr) or empty($prmTokens)">
      <xsl:sequence select="false()"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="dstStrs" as="xs:string*" select="$prmDstStr ! tokenize(.,'\s')"/>
      <xsl:sequence select="$dstStrs = $prmTokens"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

こういう関数を作っておけば、元の ahf:hasFigTitleも次のように簡素化できます.

<xsl:function name="ahf:hasFigTitle" as="xs:boolean">
  <xsl:param name="prmFig" as="element()"/>
  <xsl:sequence select="$prmFig/*[ahf:seqContainsToken(@class, ('topic/title','topic/desc'))] => exists()"/>
</xsl:function>

とっても簡単になりました. ahf:seqContainsTokenのミソは、$dstStrs = $prmTokensと一般比較演算子で左辺と右辺のシーケンスで「一つでも同じものがある?」を調べられることです.XSLT 3.0でやろうと決意していろいろ探してみると便利なのがいっぱいあります.ここで使われている=>もその一つ.また一例ですが、このようなahf:hasFigTitleの関数を作ると私はたいていこの逆の関数も作ります.

<xsl:function name="ahf:hasNoFigTitle" as="xs:boolean">
  <xsl:param name="prmFig" as="element()"/>
  <xsl:sequence select="ahf:hasFigTitle($prmFig) => not()"/>
</xsl:function>

 これもまた便利です.=> not() なんて動くの?ハイ、バッチリ動きます.XSLT 3.0で、この=>演算子と!演算子を使うと、本当にコーディングが楽しく、しかも楽にできるようになります.ご参考にしていただければ幸いです.