This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kFollowing" match="node()[not(self::h2)]"
use="generate-id(preceding-sibling::h2[1])"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="h2">
<xsl:call-template name="identity"/>
<div>
<xsl:apply-templates mode="copy"
select="key('kFollowing', generate-id())"/>
</div>
</xsl:template>
<xsl:template match="node()[not(self::h2)][preceding-sibling::h2]"/>
<xsl:template match="node()" mode="copy">
<xsl:call-template name="identity"/>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<html>
<h2>Nth title</h2>
<first-child>...</first-child> ...
<last-child>...</last-child>
<h2>N+1st title</h2> ...
<x/>
<y/>
<z/>
</html>
produces the wanted, correct result:
<html>
<h2>Nth title</h2>
<div>
<first-child>...</first-child> ...
<last-child>...</last-child>
</div>
<h2>N+1st title</h2>
<div> ...
<x></x>
<y></y>
<z></z>
</div>
</html>
Explanation:
The identity rule/template copies every node "as-is".
The identity rule is overriden for h2
elements. Here the action is to copy the h2
element and then to output a div
and inside it to apply templates (in a special mode) to all nodes (that are not h2
themselves) for which this h2
element is the first preceding-sibling h2
element.
The nodes to include in the previous step are conveniently defined as an <xsl:key>
instruction.
In order to stop the nodes that are wrapped in div
to be output again by the identity rule, we provide a template matching such nodes, that simply ignores them.