[xsl] Pattern: Processing Unwrapped Repeating Groups

Subject: [xsl] Pattern: Processing Unwrapped Repeating Groups
From: "W. Eliot Kimber" <eliot@xxxxxxxxxx>
Date: Wed, 30 Jan 2002 08:49:50 -0600
In trying to solve a problem in the style sheet I'm currently developing
I stumbled on a pattern that I hadn't seen anywhere in my reading on XSL
processing--it's probably obvious to most but it took me a while to
figure it out so I thought I would share it.

The input data consists of what I call "unwrapped repeating groups, that
is, a content model like this:

<!ELEMENT z
   (a, (b,c,d)+)
>

[I consider this style of content model to be bad form but you see it
quite a bit. My preference is to always have an intervening container
for any repeating OR or sequence groups, e.g.:

<!ELEMENT z
   (a, w+)
>
<!ELEMEMT w
   (b,d,d)
>

That is, avoid nested repeating groups. It generally makes things easier
for everybody, from authoring to processing.]

The intended presentation is as a table, e.g.:

.------------------.
| a | b1 | c1 | d1 |
|   |----|----|----|
|   | b2 | c2 | d2 |
'------------------'

The first and only "a" element starts the first row, but the 2nd..n "b"
elements start the 2nd through n rows. 

The pattern I used to solve this problem is:

<xsl:template match="z">
  <fo:table-cell starts-row="true">
    <xsl:variable name="b-elems" select="b"/>
    <xsl:attribute name="number-rows-spanned">
      <xsl:value-of select="count($b-elems)"/>
    </xsl:attribute>
    <xsl:apply-templates select="a"/>
  </fo:table-cell>
  <xsl:for-each select="b">
    <xsl:if test="position() &gt; 1">
      <fo:table-cell starts-row="true">
        <fo:block/>
      </fo:table-cell>
    </xsl:if>
    <fo:table-cell>
      <xsl:apply-templates select="."/>
    </fo:table-cell>
    <fo:table-cell>
      <xsl:apply-templates select="following::c[1]"/>
    </fo:table-cell>
    <fo:table-cell>
      <xsl:apply-templates select="following::d[1]"/>
    </fo:table-cell>
  </xsl:for-each>
</xsl:template>

I first tried doing this by generating an enclosing fo:row but found it
too combersome because you have to process the first b,c,d tupple as a
special case. Fortunately, the ability to have cells start rows
simplifies the problem. 

The key insight for me, as a novice XPath user, was the use of the
"following" axis to allow me to process the immediately following
siblings of each "b" element.

Cheers,

Eliot Kimber
ISOGEN International, LLC

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread