XSLT3.0への道(5) xsl:iterateの続き

前回xsl:iterateについて紹介しましたが、最後につけたWordMLの変換の例はxsl:tryを使ったり、saxon:assignを使ったりして、あまりにも「オタク」すぎたのでもう少し簡単な例を載せておきます.
[入力XML]
<?xml version="1.0" encoding="UTF-8" ?>
<data>
    <product type="pencil" maker="三菱"     price="100" count="5"/>
    <product type="pencil" maker="コーリン" price="90"  count="10"/>
    <product type="pencil" maker="トンボ"   price="110" count="20"/>
    <product type="pencil" maker="北星"     price="105" count="30"/>
    <product type="note"   maker="コクヨ"   price="200" count="10"/>
    <product type="note"   maker="ツバメ"   price="180" count="15"/>
    <product type="note"   maker="マルマン" price="150" count="20"/>
</data>
 
次のスタイルシートは入力の各商品ごとに、金額の計を計算します.また@type毎に小計を出力、最後に全体の計を出力します.
 
<xsl:template match="data">
    <result>
        <xsl:iterate select="product">
            <xsl:param name="previousType" select="''" as="xs:string"/>
            <xsl:param name="previousTypeAmount" select="0.00" as="xs:decimal"/>
            <xsl:param name="previousTotalAmount" select="0.00" as="xs:decimal"/>
            <xsl:variable name="amount" select="xs:decimal(@price * @count)" as="xs:decimal"/>
            <xsl:variable name="currentTotalAmount"
                          select="$previousTotalAmount + $amount" as="xs:decimal"/>
            <xsl:variable name="currentTypeAmount"
                          select="$previousTypeAmount + $amount" as="xs:decimal"/>
            <xsl:variable name="currentType" select="string(@type)" as="xs:string"/>
            <xsl:variable name="typeBreak" select="($previousType ne $currentType) and (string($previousType))" as="xs:boolean"/>
            <xsl:if test="$typeBreak">
                <total>
                    <xsl:attribute name="type" select="$previousType"/>
                    <xsl:attribute name="value" select="$previousTotalAmount"/>
                </total>
            </xsl:if>
            <xsl:copy>
                <xsl:copy-of select="@type"/>
                <xsl:copy-of select="@maker"/>
                <xsl:attribute name="total" select="$amount"/>
            </xsl:copy>
            <xsl:next-iteration>
                <xsl:with-param name="previousType" select="$currentType"/>
                <xsl:with-param name="previousTypeAmount" select="if ($typeBreak) then $amount else $currentTypeAmount"/>
                <xsl:with-param name="previousTotalAmount" select="$currentTotalAmount"/>
            </xsl:next-iteration>
            <xsl:on-completion>
                <total>
                    <xsl:attribute name="type" select="$previousType"/>
                    <xsl:attribute name="value" select="$previousTypeAmount"/>
                </total>
                <total-amount value="{$previousTotalAmount}"/>
            </xsl:on-completion>
        </xsl:iterate>
    </result>
</xsl:template>
 
xsl:on-completionはちょうど昔のCOBOLのREAD ~ AT ENDみたいなものですね.出力は次のようになります.
 
[出力XML]
<?xml version="1.0" encoding="UTF-8"?>
<result>
   <product type="pencil" maker="三菱" total="500"/>
   <product type="pencil" maker="コーリン" total="900"/>
   <product type="pencil" maker="トンボ" total="2200"/>
   <product type="pencil" maker="北星" total="3150"/>
   <total type="pencil" value="6750"/>
   <product type="note" maker="コクヨ" total="2000"/>
   <product type="note" maker="ツバメ" total="2700"/>
   <product type="note" maker="マルマン" total="3000"/>
   <total type="note" value="7700"/>
   <total-amount value="14450"/>
</result>