Re: Applying Templates

Subject: Re: Applying Templates
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Sun, 5 Nov 2000 09:39:41 +0000

> I want to be able (for this xsl file) to produce output that will first put
> down the <HEADING>,a paragraph break, then start with the first image
> (aligned left) next, put down the first <SUBHEADING> and <PARAGRAPHHEADING>
> if they exist followed by as many <PARGRAPH>'s that exist before the next
> occurence of the next <SUBHEADING> or <PARAGRAPHHEADING>.
> At that point, I need to put down the next image (aligned left again) and
> then go thru the xml again in the previous manner until I have used all of
> the images.

The first step is to translate this sequential (procedural)
description of what you want to do with a rule-based (declarative)
description.  You know your source and desired result better than I
do, but from the above description I translate it in to:

1. wherever there's a HEADING, add an 'h1' element with its content, and a
line break after it

2. wherever there's a SUBHEADING or a PARAGRAPHHEADING that doesn't
have a SUBHEADING immediately before it, add an 'img' element based on
the child element of IMAGES that has the same position within the list
of IMAGES as the SUBHEADING or PARAGRAPHHEADING does within the list

3. wherever there's a PARAGRAPH, add a 'p' element with its content

These rules don't tackle the problem of using all the images, but I'm
not sure from your description what to do if there are images left
over when there are no more SUBHEADINGs or PARAGRAPHHEADINGs to add
them before.

Once you have this declarative description, you can turn it into XSLT

1. HEADING-matching template:

<xsl:template match="HEADING">
  <h1><xsl:value-of select="." /></h1>


[The difficulty here is that we don't want to match PARAGRAPHHEADINGs
that are immediately preceded by SUBHEADINGs.  In other words, we only
want PARAGRAPHHEADINGs whose first preceding sibling is not called

<xsl:template match="SUBHEADING |
                     PARAGRAPHHEADING[name(preceding-sibling::*[1]) !=
  <xsl:variable name="count"
                select="count(preceding-sibling::SUBHEADING |
                              preceding-sibling::PARAGRAPHHEADING[name(preceding-sibling::*[1]) != 'SUBHEADING'])" />
  <xsl:variable name="image"
                select="/CONTENTAREA/IMAGES/*[position() =
                                              $count + 1]" />
  <img src="{$image/IMGPATH}" align="left" />
    <xsl:when test="SUBHEADING">
      <h2><xsl:value-of select="." /></h2>
      <h3><xsl:value-of select="." /></h3>

[And we need a template for those other PARAGRAPHHEADING elements, the
ones that *are* preceded by SUBHEADING elements.]

<xsl:template match="PARAGRAPHHEADING[name(preceding-sibling::*[1]) =
  <h3><xsl:value-of select="." /></h3>

3. PARAGRAPH-matching template

<xsl:template match="PARAGRAPH">
  <p><xsl:apply-templates /></p>

The final thing you need is something to make sure that templates
*aren't* applied to the IMAGES.  You can do this either by only
applying templates to the CONTENTITEM, or by having a null template
for IMAGES, i.e. either:

<xsl:template match="CONTENTAREA">
  <xsl:apply-templates select="CONTENTITEM" />


<xsl:template match="IMAGES" />

Of these, the former is probably better because you narrow down the
number of nodes to try to apply templates to earlier on.

I hope that this serves as a reasonable starting place for you, at
least in terms of phrasing your problem in a way that makes it more
easily soluble using XSLT. There are various other techniques that
might be appropriate, such as using a key to index into the images
that you want, or grouping the content using the Muenchian method, but
it's hard to tell from your description which are really necessary.



Jeni Tennison

 XSL-List info and archive:

Current Thread