Thank you to Martin for providing a generalized grouping algorithm.
In my previous post, I had the nesting wrong; concepts must be nested inside
of concepts not conbody elements. Here is my simplified XML:
<?xml version="1.0" encoding="UTF-8"?>
<doc>
<h1>Heading 1</h1>
<para>Some text under heading 1</para>
<para>Some text under heading 1</para>
<para>Some text under heading 1</para>
<h2>Heading 2</h2>
<h3>Heading 3</h3>
<para>Some text under heading 3</para>
<para>Some text under heading 3</para>
<h4>Heading 4</h4>
<para>Some text under heading 4</para>
</doc>
Here is my stylesheet:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:cp="http://www.frameexpert.com/functions"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:output indent="yes"
doctype-public="-//OASIS//DTD DITA 1.3 Composite//EN"
doctype-system="technicalContent/dtd/ditabase.dtd"/>
<xsl:template match="doc">
<dita><xsl:sequence select="cp:group(*)"/></dita>
</xsl:template>
<xsl:function name="cp:group" as="node()*">
<xsl:param name="elements" as="element()*"/>
<xsl:sequence select="cp:group($elements, 1)"/>
</xsl:function>
<xsl:function name="cp:group" as="node()*">
<xsl:param name="elements" as="element()*"/>
<xsl:param name="level" as="xs:integer"/>
<!-- Make a variable for the heading name. -->
<xsl:variable name="heading" select="'h' || $level"/>
<xsl:variable name="heading-next" select="'h' || $level + 1"/>
<xsl:for-each-group select="$elements"
group-starting-with="*[local-name() = $heading]">
<xsl:choose>
<xsl:when test="self::*[local-name() = $heading]">
<concept id="{generate-id()}">
<title>
<xsl:apply-templates/>
</title>
<xsl:sequence select="cp:group(current-group()
except ., $level + 1)"/>
</concept>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="para">
<p><xsl:apply-templates/></p>
</xsl:template>
<xsl:mode on-no-match="shallow-copy"/>
</xsl:stylesheet>
Here is my result:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dita
PUBLIC "-//OASIS//DTD DITA 1.3 Composite//EN"
"technicalContent/dtd/ditabase.dtd">
<dita>
<concept id="d1e3">
<title>Heading 1</title>
<p>Some text under heading 1</p>
<p>Some text under heading 1</p>
<p>Some text under heading 1</p>
<concept id="d1e15">
<title>Heading 2</title>
<concept id="d1e19">
<title>Heading 3</title>
<p>Some text under heading 3</p>
<p>Some text under heading 3</p>
<concept id="d1e28">
<title>Heading 4</title>
<p>Some text under heading 4</p>
</concept>
</concept>
</concept>
</concept>
</dita>
However, I need content under a title to be wrapped in a conbody element,
like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dita
PUBLIC "-//OASIS//DTD DITA 1.3 Composite//EN"
"technicalContent/dtd/ditabase.dtd">
<dita>
<concept id="d1e3">
<title>Heading 1</title>
<conbody>
<p>Some text under heading 1</p>
<p>Some text under heading 1</p>
<p>Some text under heading 1</p>
</conbody>
<concept id="d1e15">
<title>Heading 2</title>
<concept id="d1e19">
<title>Heading 3</title>
<conbody>
<p>Some text under heading 3</p>
<p>Some text under heading 3</p>
</conbody>
<concept id="d1e28">
<title>Heading 4</title>
<conbody>
<p>Some text under heading 4</p>
</conbody>
</concept>
</concept>
</concept>
</concept>
</dita>
I can simplify things by always adding a conbody (even if it's empty), but I
would like to understand the better solution. I tried group-ending-with, but
I couldn't get it to work.
Thanks in advance! This is such a helpful group.
Rick
|