XSLT 2.0で便利になった機能(41) treat as演算子

今回はtreat as演算子を紹介します.treat as演算子はちょっと難しい概念の中で定義されています.それは、XPathの式の処理系のインプリメントに関係しています.
 
XPathの式処理は、勧告によれば「static analysis phase」と「dynamic evaluation phase」の二つのフェーズから構成されているとされます.「static analysis phase」は文字通り"静的"に式を評価します.この際スキーマでvalidationされた入力データがどうであろうが関係ありません.式自体が解釈/評価されます.「dynamic evaluation phase」は、データが入力され式の値が実際に計算される際に行われます.
例えば次のようなデータを考えてみます.
 
[入力データ]
<?xml version="1.0" encoding="UTF-8" ?>
<data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="input_schema.xsd">
    <product type="pencil" maker="三菱"     unitPrice="100"/>
    <product type="pencil" maker="コーリン" unitPrice="90"/>
    <product type="pencil" maker="トンボ"   unitPrice="110"/>
    <product type="pencil" maker="北星"     unitPrice="105"/>
    <product type="note"   maker="コクヨ"   unitPrice="NA"/>
    <product type="note"   maker="ツバメ"   unitPrice="NA"/>
    <product type="note"   maker="マルマン" unitPrice="NA"/>
</data>
 
[スキーマ]
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="data" type="dataType"/>
    <xs:complexType name="dataType">
        <xs:sequence>
            <xs:element ref="product" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
    <xs:element name="product" type="productType"/>
    <xs:complexType name="productType">
        <xs:attribute name="type" type="xs:string"/>
        <xs:attribute name="maker" type="xs:string"/>
        <xs:attribute name="unitPrice" type="unitPriceType"/>
    </xs:complexType>
    <xs:simpleType name="unitPriceType">
        <xs:union memberTypes="NotAvailableType xs:positiveInteger" />
    </xs:simpleType>
    <xs:simpleType name="NotAvailableType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="NA"/>
        </xs:restriction>
    </xs:simpleType>
</xs:schema>
 
このときdata(@unitPrice)は、どうなるでしょうか?「static analysis phase」では、スキーマに従えば、unitPriceTypeになります.ですから、第一パラメータに明示的にxs:positiveIntegerを要求するような次のような関数に渡すとすればエラーを報告すると考えられます.
 
tmf:calcPrice(data(@unitPrice),2)
 
<xsl:function name="tmf:calcPrice" as="xs:string">
    <xsl:param name="prmUnitPrice" as="xs:positiveInteger"/>
    <xsl:param name="prmNumber"    as="xs:integer"/>
    <xsl:sequence select="string($prmUnitPrice * $prmNumber)"/>
</xsl:function>
 
treat as演算子はこのような場合、
 
tmf:calcPrice(data(@unitPrice) treat as xs:positiveInteger, 2)
 
とするとstatic typeとしてオペランドのxs:positiveIntegerを返します.ですのでこの関数呼び出しの記述は「static analysis phase」をパスします.
 
「dynamic evaluation phase」ではどうでしょうか?上記の例では、data(@unitPrice)がxs:positiveIntegerにマッチすれば、その値を返します.もし万が一マッチしない場合、dynamic errorが報告されます.つまりここでは、treat asは、C++言語のassert()のような役割を果たしてくれます.
試しに
 
<xsl:template match="product">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:attribute name="price"
                       select="if (data(@price) instance of NotAvailableType)
                               then
                                    string('0')
                               else
                                    tmf:calcPrice(data(@unitPrice) treat as xs:positiveInteger,2)"/>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>
 
としてみます.上記の例のデータでは動作します.ではスキーマとデータの一部を次のように変えてみます.
 
[スキーマ]
    <xs:simpleType name="unitPriceType">
        <xs:union memberTypes="OutOfStockType NotAvailableType xs:positiveInteger" />
    </xs:simpleType>
    <xs:simpleType name="NotAvailableType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="NA"/>
        </xs:restriction>
    </xs:simpleType>
    <xs:simpleType name="OutOfStockType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="OS"/>
        </xs:restriction>
    </xs:simpleType>
 
[入力データ]
    <product type="note"   maker="コクヨ"   unitPrice="OS"/>
 
実行してみると、
 
Couldn't cast to destination type - found 'OS' of type User defined, expected ty
pe xs:positiveInteger - xs:positiveInteger
 
とちゃんとエラーになります.(XPathで定義されているXPDY0050とは違うところが心配ですが...)
いずれにしてもtreat asはschema-awareでstatic type chekingを行うXSLTプロセッサを使ってXMLスキーマでvalidationした入力データを扱う局面で威力を発揮するようですね.
 
#今回もScema-awareなXSLTプロセッサでないと意味がないので、Altova XMLを使ってみました.しかしヘルプを見ると、"The optional static type checking feature is not supported."となっていて、Altova XMLでは「static analysis phase」はサポートされていないようです.