[xsl] Extracting a fragment between milestones from a complex structure.

Subject: [xsl] Extracting a fragment between milestones from a complex structure.
From: Jeroen Hellingman <jeroen@xxxxxxxx>
Date: Mon, 28 Feb 2011 23:00:10 +0100
I am looking for a template which allows me to extract a single page, as
marked by milestone elements <pb n="12"/> from a complex structure. Note
that the milestone elements may occur in different parents. I want all
the parent nodes, and everything between the <pb n="12"/> and the next
<pb/> element

For example, when applying the extract method with as parameter "12" on
the following sample

<TEI>
    <front>...</front>
    <body>
        <div1>
            <head>Blah</head>
            Blah blah <pb n="12"/> blah blah.
        </div1>
        <div1>
            Blah blah blah blah.
        </div1>
        <div1>
            Blah <hi>blah <pb n="13"/> blah</hi> blah.
        </div1>
    </body>
    <back>...</back>
</TEI>

It should return:

<TEI>
    <body>
        <div1>
            <pb n="12"/> blah blah.
        </div1>
        <div1>
            Blah blah blah blah.
        </div1>
        <div1>
            Blah <hi>blah <pb n="13"/></hi>
        </div1>
    </body>
</TEI>

The idea is that it will copy only elements that are a ancestor of
either <pb/> and follow the first <pb/> and precede the second <pb/>.
The problem I face is formulating a nice XPath to select those elements.

What I have so-far is a few templates that certainly do not behave as
expected:

    <xsl:template name="extract-page">
        <xsl:param name="n"/>

            <xsl:call-template name="extract-from-to">
                <xsl:with-param name="from" select="//pb[@n=$n]"/>
                <xsl:with-param name="to"
select="(//pb[preceding::pb[@n=$n]])[1]"/>
            </xsl:call-template>

    </xsl:template>

    <xsl:template name="extract-from-to">
        <xsl:param name="from"/>
        <xsl:param name="to"/>

            <xsl:copy-of select="$from"/>
            <xsl:copy-of select="$to"/>

            <xsl:apply-templates mode="copy"
                select="//*[(ancestor::node()=$from and
ancestor::node()=$to) or (following::node()=$from and
preceding::node()=$to)]">
            </xsl:apply-templates>
    </xsl:template>


    <xsl:template mode="copy" match="text()|*">
        <xsl:copy>
            <xsl:apply-templates mode="copy"/>
        </xsl:copy>
    </xsl:template>


Any hints or suggestions will be appreciated.

Jeroen Hellingman

Current Thread