XSLT 2.0で便利になった機能(49) node()*とdocument-node() (その2)

先だって
 
<?xml version="1.0" encoding="UTF-8" ?>
<div>
    <span>りんご</span>
    <span>みかん</span>
    <span>バナナ</span>
</div>
 
と書いて
 
<xsl:variable name="div_seq" as="node()*">
    <xsl:sequence select="/div/span[1]"/>
    <xsl:sequence select="/div/span[2]"/>
    <xsl:sequence select="/div/span[3]"/>
</xsl:variable>
 
と書くと、node()*には元のツリーのノードが保持されると書きました.XSLTではgenerate-id()というノードを識別するidを返す関数があるので、これを使って試してみます.
 
<xsl:message select="'/div/span[1]=',generate-id(/div/span[1]),' contents=',/div/span[1]"/>
<xsl:message select="'/div/span[2]=',generate-id(/div/span[2]),' contents=',/div/span[2]"/>
<xsl:message select="'/div/span[3]=',generate-id(/div/span[3]),' contents=',/div/span[3]"/>
<xsl:message select="'$div_seq[1]=',generate-id($div_seq[1]),' contents=',$div_seq[1]"/>
<xsl:message select="'$div_seq[2]=',generate-id($div_seq[2]),' contents=',$div_seq[2]"/>
<xsl:message select="'$div_seq[3]=',generate-id($div_seq[3]),' contents=',$div_seq[3]"/>
 
とやってみると、出力は
 
 /div/span[1]= d2e3  contents=<span>りんご</span>
 /div/span[2]= d2e6  contents=<span>みかん</span>
 /div/span[3]= d2e9  contents=<span>バナナ</span>
 $div_seq[1]= d2e3  contents=<span>りんご</span>
 $div_seq[2]= d2e6  contents=<span>みかん</span>
 $div_seq[3]= d2e9  contents=<span>バナナ</span>
 
となります.確かにnode()*はソースのツリーを指しています.
では、
 
<xsl:variable name="div_ns" as="document-node()">
    <xsl:document>
        <xsl:sequence select="/div/span[1]"/>
        <xsl:sequence select="/div/span[2]"/>
        <xsl:sequence select="/div/span[3]"/>
    </xsl:document>
</xsl:variable>
 
とやるとどうなるのでしょうか?
この場合
 
<xsl:message select="'$div_ns/*[1]=',generate-id($div_ns/*[1]),' contents=',$div_ns/*[1]"/>
<xsl:message select="'$div_ns/*[2]=',generate-id($div_ns/*[2]),' contents=',$div_ns/*[2]"/>
<xsl:message select="'$div_ns/*[3]=',generate-id($div_ns/*[3]),' contents=',$div_ns/*[3]"/>
 
とやると、
 
 $div_ns/*[1]= d3e1  contents=<span>りんご</span>
 $div_ns/*[2]= d3e3  contents=<span>みかん</span>
 $div_ns/*[3]= d3e5  contents=<span>バナナ</span>
 
となります.
 
つまり<xsl:document>~</xsl:document>の中ではノードがコピーされて新しくツリーが構築されます.元のツリーの兄弟関係はなくなり、新たなノードで再構築されます.ここはすごく大事なところです.こうなってくれないと、テンプレート間でテンポラリーツリーを更新しながら受け渡すような再帰的なプログラムはうまく動いてくれません.
 
実際
 
<xsl:variable name="div_ns" as="document-node()">
    <xsl:document>
        <xsl:sequence select="/div/span[1]"/>
        <xsl:sequence select="/div/span[2]"/>
        <xsl:sequence select="/div/span[3]"/>
        <xsl:sequence select="/div/span[1]"/>
    </xsl:document>
</xsl:variable>
 
として、二回ノードをコピーすると
 
<xsl:message select="'$div_ns/*[3]=',generate-id($div_ns/*[3]),' contents=',$div_ns/*[3]"/>
<xsl:message select="'$div_ns/*[4]=',generate-id($div_ns/*[4]),' contents=',$div_ns/*[4]"/>
<xsl:message select="'$div_ns/*[3]/following-sibling::*[1]=',$div_ns/*[3]/following-sibling::*[1]"/>
<xsl:message select="'generate-id($div_ns/*[3]/following-sibling::*[1])=',generate-id($div_ns/*[3]/following-sibling::*[1])"/>
 
 
 $div_ns/*[3]= d3e5  contents=<span>バナナ</span>
 $div_ns/*[4]= d3e7  contents=<span>りんご</span>
 $div_ns/*[3]/following-sibling::*[1]=<span>りんご</span>
 generate-id($div_ns/*[3]/following-sibling::*[1])= d3e7
 
と出力されます.ツリーが再構築されているのがわかります.このような点もnode()*とdocument-node()は決定的に違います.
 
先ほど言及したテンプレート間でテンポラリーツリーを更新しながら受け渡すような再帰的なプログラムでは、やはりdocument-node()が便利です.node()*を使ってできないことはないでしょうが、やはり向いていないでしょう.