This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
Re: Nested sections from flat structure
- To: Linda van den Brink <lvdbrink at baan dot nl>
- Subject: Re: [xsl] Nested sections from flat structure
- From: Jeni Tennison <mail at jenitennison dot com>
- Date: Mon, 14 May 2001 11:30:00 +0100
- CC: "Xsl-List (E-mail)" <xsl-list at lists dot mulberrytech dot com>
- Organization: Jeni Tennison Consulting Ltd
- References: <41F966E6F6E5D311AFE000508B6FC21E052616F2@ex-nld-u2.baan.com>
- Reply-To: xsl-list at lists dot mulberrytech dot com
Hi Linda,
> My source documents are a bit troublesome. They have a structure
> like HTML. There are two elements that indicate start of a section,
> <head> and <a name="">. Both appear inside a p element. A source
> document can be:
> - without subsections
> - with subsections indicated by <head>
> - with subsections indicated by <a name="">
> - with subsections indicated by <a name="">, and <head> subsections
> inside the first subsections
> - with subsections indicated by <head>, and <a name=""> subsections
> inside the first subsections
When you have troublesome source then it's sometimes better to take a
two-pass approach on the problem - transform it into something more
approachable, and then transform *that* into the output that you want.
Your source would be a lot more approachable if it was clear what was
a 'top' section and what were sections underneath them. In the
following stylesheet, I first convert the source into:
<GeneralSection/>
<Paragraph>first some intro text</Paragraph>
<TopSection/>
<SectionTitle>A</SectionTitle>
<Paragraph>goes with A.</Paragraph>
<SubSection ID="A.1"/>
<Paragraph>Text with A.1</Paragraph>
<Paragraph>More text with A.1</Paragraph>
<SubSection ID="A.2"/>
<Paragraph>text with A.2</Paragraph>
<Paragraph>Goes with A.2</Paragraph>
<TopSection/>
<SectionTitle>B</SectionTitle>
<Paragraph>Goes with B.</Paragraph>
<Paragraph>Goes with B.</Paragraph>
<TopSection/>
<SectionTitle>C</SectionTitle>
<Paragraph>Goes with C</Paragraph>
<Paragraph>Goes with C</Paragraph>
And from there using the Muenchian method to create the hierarchy.
The advantage is that both transformations are relatively
straight-forward; the disadvantage is that you need to either use a
node-set extension in your stylesheet or split the stylesheet in two
and use the second on the output of the first.
If you want to use a one-pass approach, like your stylesheet is at the
moment, the only suggestion I'd make for simplifying it is to split up
all those xsl:for-eaches into separate templates, using modes to
distinguish between the different types of processing you do within
each.
Anyway, here's my approach (unless you're using a processor that
supports exsl:node-set(), you'll need to change the namespace for the
node-set() function):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:variable name="top" select="topic/text/p[head or a/@name][1]" />
<xsl:template match="/">
<xsl:variable name="sections">
<GeneralSection />
<xsl:apply-templates mode="convert" />
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($sections)/GeneralSection"
mode="group" />
</xsl:template>
<xsl:template match="p" mode="convert">
<Paragraph><xsl:apply-templates /></Paragraph>
</xsl:template>
<xsl:template match="p[head]" mode="convert">
<xsl:choose>
<xsl:when test="$top/head"><TopSection /></xsl:when>
<xsl:otherwise><SubSection /></xsl:otherwise>
</xsl:choose>
<SectionTitle><xsl:apply-templates select="head" /></SectionTitle>
<Paragraph>
<xsl:apply-templates select="node()[not(self::head)]" />
</Paragraph>
</xsl:template>
<xsl:template match="p[a/@name]" mode="convert">
<xsl:choose>
<xsl:when test="$top/a/@name">
<TopSection ID="{a/@name}" />
</xsl:when>
<xsl:otherwise><SubSection ID="{a/@name}" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:key name="children" match="Paragraph | SectionTitle"
use="generate-id(preceding-sibling::*
[self::GeneralSection or self::TopSection or
self::SubSection][1])" />
<xsl:key name="children" match="TopSection"
use="generate-id(preceding-sibling::GeneralSection[1])" />
<xsl:key name="children" match="SubSection"
use="generate-id(preceding-sibling::TopSection[1])" />
<xsl:template match="GeneralSection | TopSection | SubSection"
mode="group">
<GeneralSection>
<xsl:copy-of select="@*" />
<xsl:apply-templates select="key('children', generate-id())"
mode="group" />
</GeneralSection>
</xsl:template>
<xsl:template match="Paragraph" mode="group">
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>
I hope that helps,
Jeni
---
Jeni Tennison
http://www.jenitennison.com/
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list