DITA-OTのバージョンを比較する

USの同僚から「お前のDITA-OTのプラグイン、DITA-OT1.8.5でやるとこけるで~!」と連絡がありました.私はずっと1.8.4を使っていたので気が付きませんでしたが、確かに1.8.5でやると一発でこけます.原因を調べてみると、1.8.5からはlq/@hrefの処理をDITA-OTが行うように変更されていました.1.8.4までは、ここに何を書こうがDITA-OTは何もやってくれなかったのです.(仕方がないので自前で処理していました)ところが1.8.5からは別の処理を付け加えねばなりません.ということはプラグインはDITA-OTのバージョンにより処理を分岐させる必要があります.でも1.8.4と1.8.5というようなバージョン表記だと大小比較は簡単な文字列比較ではできませんよね.という訳でDITA-OTのバージョンの比較用の関数を作ってみました.

    <!-- 
     function: Dispaly version compare result.
     param: prmVersion1: Version 1
                prmVersion2: Version 2 
     return: none
     note:      
    -->
    <xsl:function name="ahf:dispCompareOtVersion" as="xs:string">
        <xsl:param name="prmVer1" as="xs:string"/>
        <xsl:param name="prmVer2" as="xs:string"/>
        <xsl:variable name="compareResult" as="xs:integer?" select="ahf:compareOtVersion($prmVer1,$prmVer2)"/>
        <xsl:choose>
            <xsl:when test="empty($compareResult)">
                <xsl:sequence select="concat($prmVer1,' と ',$prmVer2,' は比較できません!')"/>
            </xsl:when>
            <xsl:when test="$compareResult eq 1">
                <xsl:sequence select="concat($prmVer1,' gt ',$prmVer2)"/>
            </xsl:when>
            <xsl:when test="$compareResult eq -1">
                <xsl:sequence select="concat($prmVer1,' lt ',$prmVer2)"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence select="concat($prmVer1,' eq ',$prmVer2)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>        

    <!-- 
     function: Compare DITA-OT version.
     param: prmVersion1: Version 1
                prmVersion2: Version 2 
     return: xs:integer?
                -1: Version 1 < Version 2
                 0: Version 1 = Version 2
                 1: Version 1 > Version 2
                 (): Error
     note:      
    -->
    <xsl:function name="ahf:compareOtVersion" as="xs:integer?">
        <xsl:param name="prmVer1" as="xs:string"/>
        <xsl:param name="prmVer2" as="xs:string"/>
        <!-- バージョンを"."で区切ってxs:string*にする -->
        <xsl:variable name="ver1Seq" as="xs:string*">
            <xsl:analyze-string select="$prmVer1" regex="[\.]">
                <xsl:matching-substring/>
                <xsl:non-matching-substring>
                    <xsl:sequence select="."/>
                </xsl:non-matching-substring>
            </xsl:analyze-string>
        </xsl:variable>
        <xsl:variable name="ver2Seq" as="xs:string*">
            <xsl:analyze-string select="$prmVer2" regex="[\.]">
                <xsl:matching-substring/>
                <xsl:non-matching-substring>
                    <xsl:sequence select="."/>
                </xsl:non-matching-substring>
            </xsl:analyze-string>
        </xsl:variable>
        <!--xs:string*を整数として扱って比較する-->
        <xsl:sequence select="ahf:compareOtVersionNo($ver1Seq,$ver2Seq)"/>
    </xsl:function>
    
    <!-- 
     function: Compare DITA-OT version No.
     param: prmVersion1: Version 1
                prmVersion2: Version 2 
     return: xs:integer?
                -1: Version 1 < Version 2
                 0: Version 1 = Version 2
                 1: Version 1 > Version 2
                 (): Error
     note:      
    -->
    <xsl:function name="ahf:compareOtVersionNo" as="xs:integer?">
        <xsl:param name="prmVer1" as="xs:string*"/>
        <xsl:param name="prmVer2" as="xs:string*"/>
        
        <xsl:choose>
            <xsl:when test="exists($prmVer1[1]) and exists($prmVer2[1])">
                <xsl:variable name="verNo1" as="xs:integer?" select="ahf:getOtVersionNo($prmVer1[1])"/>
                <xsl:variable name="verNo2" as="xs:integer?" select="ahf:getOtVersionNo($prmVer2[1])"/>
                <xsl:choose>
                    <!--両方とも整数値であれば整数で比較-->
                    <xsl:when test="exists($verNo1) and exists($verNo2)">
                        <xsl:choose>
                            <xsl:when test="$verNo1 gt $verNo2">
                                <xsl:sequence select="1"/>
                            </xsl:when>
                            <xsl:when test="$verNo1 lt $verNo2">
                                <xsl:sequence select="-1"/>
                            </xsl:when>
                            <xsl:otherwise>
                                <!--値が同じとき-->
                                <xsl:choose>
                                    <xsl:when test="exists($prmVer1[position() gt 1]) or exists($prmVer2[position() gt 1])">
                                        <!--いずれかが残りがあれば再帰的に比較-->
                                        <xsl:sequence select="ahf:compareOtVersionNo($prmVer1[position() gt 1],$prmVer2[position() gt 1])"/>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <!--残りがなければ値は等しい-->
                                        <xsl:sequence select="0"/>
                                    </xsl:otherwise>
                                </xsl:choose>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:when>
                    <xsl:otherwise>
                        <!--整数として扱えなければ比較できない-->
                        <xsl:sequence select="()"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <!-- 片方だけ残りがあればもう一方を"0"として比較する -->
            <xsl:when test="exists($prmVer1[1]) and empty($prmVer2[1])">
                <xsl:sequence select="ahf:compareOtVersion($prmVer1[1],'0')"/>
            </xsl:when>
            <xsl:when test="empty($prmVer1[1]) and exists($prmVer2[1])">
                <xsl:sequence select="ahf:compareOtVersion('0',$prmVer2[1])"/>
            </xsl:when>
            <!-- 両方とも整数でなければ比較できない -->
            <xsl:otherwise>
                <xsl:sequence select="()"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>
    
    <!-- 
     function: Get OT version number.
     param: prmVersionNo
     return: xs:integer?
     note:      DITA-OTのバージョン番号にはまれに"M2"などという文字列が入るので"M"は除外して比較するようにする.
                整数にキャストできなければ空シーケンスを返す.
    -->
    <xsl:function name="ahf:getOtVersionNo" as="xs:integer?">
        <xsl:param name="prmVerNo" as="xs:string"/>
        <xsl:variable name="verNo" as="xs:string" select="replace($prmVerNo,'^M([0-9+])','$1')"/>
        <xsl:choose>
            <xsl:when test="$verNo castable as xs:integer">
                <xsl:sequence select="xs:integer($verNo)"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence select="()"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>

こうしておいて

    <xsl:template match="/">
        <xsl:message select="ahf:dispCompareOtVersion('1.8.5','1.8.5')"/>
        <xsl:message select="ahf:dispCompareOtVersion('1.8.5.0','1.8.5')"/>
        <xsl:message select="ahf:dispCompareOtVersion('1.8.5','1.8.4')"/>
        <xsl:message select="ahf:dispCompareOtVersion('1.8.5','2.0')"/>
        <xsl:message select="ahf:dispCompareOtVersion('1.8.5','1.8.M2')"/>
        <xsl:message select="ahf:dispCompareOtVersion('1.8.2.0','1.8.M2')"/>
        <xsl:message select="ahf:dispCompareOtVersion('1.8.5','1.8x.2x')"/>
    </xsl:template>


で呼び出すとoXygenでは次のように表示されます.

[Saxon-PE] 1.8.5 eq 1.8.5
[Saxon-PE] 1.8.5.0 eq 1.8.5
[Saxon-PE] 1.8.5 gt 1.8.4
[Saxon-PE] 1.8.5 lt 2.0
[Saxon-PE] 1.8.5 gt 1.8.M2
[Saxon-PE] 1.8.2.0 eq 1.8.M2
[Saxon-PE] 1.8.5 と 1.8x.2x は比較できません!

この関数を使って無事に1.8.5とそれ未満のDITA-OTを識別して変換するコードを書くことができました.