[xsl] xsl:for-each-group: start groups depending on number of group members?

Subject: [xsl] xsl:for-each-group: start groups depending on number of group members?
From: Yves Forkl <Y.Forkl@xxxxxx>
Date: Fri, 27 Apr 2007 19:18:26 +0200
Relying heavily on xsl:for-each-group in the context of rather complicated transformations, I have run into this situation (and am stuck there...): When specifying, in group-starting-with, the pattern that may start some specific group, I would like to take into account the number of items available for grouping.

More precisely, my group starting pattern must include a filter using preceding-sibling which I do not want to "look beyond" the current sequence of items to be grouped.

Showcasing this in a minimal example (more details below):

INPUT:

<root>
  <A/>
  <sub>a1</sub>
  <B/>
  <sub>b1</sub>
  <sub>b2</sub>
  <sub>b3</sub>
  <C/>
  <sub>c1</sub>
  <sub>c2</sub>
</root>

OUTPUT:

<B_new>
   <sub_new>b1</sub_new>
   <sub_new>b2</sub_new>
   <sub_new>b3</sub_new>
</B_new>

STYLESHEET:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                version="2.0">

<xsl:output indent="yes"/>

  <xsl:template match="/">
    <xsl:for-each-group
      select="root/*"
      group-starting-with="A|B|C">
      <xsl:apply-templates
        select="current-group()[1]"
        mode="groups_at_root_level"/>
    </xsl:for-each-group>
  </xsl:template>

  <!-- don't care for them now -->
  <xsl:template match="A|C" mode="groups_at_root_level"/>

  <xsl:template match="B" mode="groups_at_root_level">
    <B_new>
      <xsl:variable name="items_total" select="count(current-group())"/>
      <xsl:for-each-group
        select="current-group()"
        group-starting-with="
          B|
          sub[not(
            preceding-sibling::*[position() lt $items_total][A])]">
        <xsl:apply-templates select="current-group()"/>
      </xsl:for-each-group>
    </B_new>
  </xsl:template>

  <xsl:template match="sub">
    <sub_new>
      <xsl:apply-templates/>
    </sub_new>
  </xsl:template>

<xsl:template match="*"/>

</xsl:stylesheet>


I would like to ask your opinion on the preceding-sibling line. If it just read preceding-sibling::A, the output would of course miss all "sub" instances. (Unfortunately, for reasons difficult to explain here, I can't just drop the negated preceding-sibling filter because I need it in some of my group starting patterns.)


Obviously, my above try at restricting the number of preceding siblings to consider to those which are among the items to group is rather limited: When xsl:for-each-group examines the very first item, the first preceding sibling of that will already be outside of the groupable items (while the last item will have many more "acceptable" preceding siblings than the first). This makes me dream of a kind of dynamic position counter returning the position of the current item that xsl:for-each-group is trying to put into a group. Is there such a thing?

Alternatively, how could I restrict sibling tests to the items which are being grouped, inside the pattern of group-starting-with?

Yves

Current Thread