Re: [xsl] select immediately following siblings with constraints?

Subject: Re: [xsl] select immediately following siblings with constraints?
From: "James A. Robinson" <jim.robinson@xxxxxxxxxxxx>
Date: Fri, 24 Feb 2006 02:20:11 -0800
> At 01:54 PM 2/23/2006, you wrote:
> >Thank you very much for the reply.  I actually hadn't been aware of
> >the << and >> operators, I shall have to read up on them.
> Yes, they're fun, and ought to be useful for all kinds of neat things 
> we haven't discovered yet.

Yes indeed!  So I ended up using <<, it seems to be exactly what I
wanted.  It is still processing elements unnecessarily because my
expression has it testing all following-siblings, but hopefully the
testing of position in document order is less intensive then doing all
that unnecessary grouping.

I decided to make it more readable (to me, at least) by splitting the
creation of a $stop node from the processing of the following-sibling

Here's the complete solution for my merging problem:

    Recursively merge adjoining sections into a single section.
    We test $sections to see if there is any instance of a
    section whose first preceding-sibling element is a section
    with an identical id.
    If the test is true, we recursively call ourselves, passing
    in the document returned by the merged_toc template, which
    merges adjacent sections sharing a section id.
  <xsl:template name="merge_sections">
    <xsl:param name="sections" as="document-node()" />
      <xsl:when test="exists($sections//section[@id = preceding-sibling::element()[1][self::section]/@id])">
        <xsl:call-template name="merge_sections">
          <xsl:with-param name="sections">
            <xsl:apply-templates select="$sections" mode="merged_toc" />
        <xsl:sequence select="$sections" />

    Merge adjoining sections sharing a section id into a single group,
    skipping sections we infer were previously handled.
  <xsl:template match="section" mode="merged_toc" as="element(section)?">
      If the immediately preceding element is a section which has the
      same id, it should have already been handled. Otherwise we want
      to copy it.
    <xsl:if test="not(preceding-sibling::element()[1][self::section][@id = current()/@id])">
        <xsl:sequence select="@*" />
        <xsl:apply-templates select="*" mode="#current" />

          Now we want to pull in the children of any consecutive sequence
          of immediately following sibling sections which have the same id
          as the current section.

          We first test to see if the immediately following element is
          a section with the same id as the current section. If this
          isn't the case, we know we aren't supposed to do any merges.

          If we pass the test, we construct $stop, setting it to the
          first following-sibling::element() which is not a section
          with an identical section id to our own.

          If $stop has a value, we apply templates to following-sibling
          sections which are positioned, in document order, before $stop.
          If $stop does not have a value, all the following-sibling
          sections share our section id, and we can merge them all.
        <xsl:if test="following-sibling::element()[1][self::section][@id = current()/@id]">
          <xsl:variable name="stop"
                      self::article or self::section[@id != current()/@id]][1]" />
            <xsl:when test="$stop">
              <xsl:apply-templates select="following-sibling::section[. &lt;&lt; $stop]/*" mode="#current" />
              <xsl:apply-templates select="following-sibling::section/*" mode="#current" />

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
James A. Robinson                       jim.robinson@xxxxxxxxxxxx
Stanford University HighWire Press
+1 650 7237294 (Work)                   +1 650 7259335 (Fax)

Current Thread