直前のノードが無視可能か調べる!

DITAというのは、うまく言えませんが非常にルーズな側面があります.例えば、セクション(section)のデータモデルは、
 
( text data or ph or codeph or synph or filepath or msgph or userinput or systemoutput or b or u or
i or tt or sup or sub or uicontrol or menucascade or term or xref or cite or q or boolean or state or
keyword or option or parmname or apiname or cmdname or msgnum or varname or wintitle or tm
or p or lq or note or dl or parml or ul or ol or sl or pre or codeblock or msgblock or screen or lines
or fig or syntaxdiagram or imagemap or image or object or table or simpletable or title or
draft-comment or required-cleanup or fn or indextermref or indexterm or data or data-about or
foreign or unknown) (any number)
 
です.DTDで言えば、sectionの直下に#PCDATAが書けてしまいます.DocBookのsectionは基本的にtitleがあってあとはparaなどの要素があってと、#PCDATAを許していないのとは大変な違いです.
これが元で、ある要素のテンプレートで、直前のノードがすべて無視可能かどうか調べる必要が出てきてしまいました.直前のノードはXPathだとpreceding-sibling::node()なのですが、これが無視可能か否かは以下の条件によります.
 
1.white-spaceからなるテキストノードは無視可能
2.コメントノードも無視可能
3.処理命令ノードも無視可能
4.要素だったら無視できない
 
ところでこれをどうやって調べたら良いか考えていたら頭が痛くなってしまいました.
例えば、(normalize-space(string(.)) eq '') and (name(.) eq '')ではダメなのです、テキストノードはこれで良いのですが、コメントノードと処理命令ノードは、string(.)はその内容を返してしまうからです.
そもそもDOMにはnodeTypeというのがあるのですが、XPathには見当たりません.node-kind()というのがXQuery and XPath Data Model 3.0(http://www.w3.org/TR/xpath-datamodel-30/#dm-node-kind)に定義されているのですが、残念ながら今は使える術がありません.ましてやDITA Open toolkitのSaxonは二世代前の9.1Bです.
では、どうするか?次のようにやってみました.
 
[ある要素のテンプレートのローカル変数]
<xsl:variable name="isPrecedingIgnorableNode" as="xs:boolean">
    <xsl:choose>
        <xsl:when test="preceding-sibling::node()[1]">
            <xsl:apply-templates select="preceding-sibling::node()[1]" mode="CHECK_IGNORABLE"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:sequence select="true()"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:variable>
 
[チェックするテンプレート]
<xsl:template match="text()" mode="CHECK_IGNORABLE">
    <xsl:choose>
        <xsl:when test="normalize-space(.) eq ''">
            <xsl:choose>
                <xsl:when test="preceding-sibling::node()[1]">
                    <xsl:apply-templates select="preceding-sibling::node()[1]" mode="CHECK_IGNORABLE"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:sequence select="true()"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
            <xsl:sequence select="false()"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
<xsl:template match="processing-instruction()" mode="CHECK_IGNORABLE">
    <xsl:choose>
        <xsl:when test="preceding-sibling::node()[1]">
            <xsl:apply-templates select="preceding-sibling::node()[1]" mode="CHECK_IGNORABLE"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:sequence select="true()"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
<xsl:template match="comment()" mode="CHECK_IGNORABLE">
    <xsl:choose>
        <xsl:when test="preceding-sibling::node()[1]">
            <xsl:apply-templates select="preceding-sibling::node()[1]" mode="CHECK_IGNORABLE"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:sequence select="true()"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
<xsl:template match="element()" mode="CHECK_IGNORABLE">
    <xsl:sequence select="false()"/>
</xsl:template>
 
$isPrecedingIgnorableNode変数には、preceding-sibling::node()[1]を順に調べてtrue()/false()が設定されます.このスタイルシートはXSLT2.0ですが、戻り値を"true"/"false"のようにすればXSLT1.0でもなんとかなります.
しかしXPathには以外と肝心な関数が欠けていたんだなと思いました.