Re: [xsl] xsl:for-each-group help needed !

Subject: Re: [xsl] xsl:for-each-group help needed !
From: "Christophe Marchand cmarchand@xxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Thu, 17 Sep 2020 14:53:51 -0000
My goal wasn't to start a competition between best XSL coders ! I do not have enough prices for awards !

But, indeed, this use case may occurs. But I've decided to let the writer correct what he has done by hand.
To be honest, our editor can not generate such things... but writers may modify generated files, and try to change width values...


Thanks again, based on Gerrit solution, I have now something that correctly works.

Le 17/09/2020 C 16:26, Imsieke, Gerrit, le-tex gerrit.imsieke@xxxxxxxxx a C)critB :
Hi Martin,

Does your code handle this input?

<blocks>
B  <block id="i1">content</block>
B  <block id="i2" width="33">content</block>
B  <block id="i3" width="67">content</block>
B  <block id="i4" width="50">content</block>
B  <block id="i5" width="50">content</block>
B  <block id="i6" width="25">content</block>
B  <block id="i6a" width="80">content</block>
B  <block>content</block>
B  <block id="i7" width="55">content</block>
B  <block id="i8" width="20">content</block>
B  <block id="i9">content</block>
</blocks>

i6 and i6a, taken together, have a width of 105. I guess then i6a needs to be on a new line.

I introduced an accumulator that can have two values:

<xsl:accumulator name="width" as="xs:integer+" initial-value="0">
B  <xsl:accumulator-rule match="blocks" select="0"/>
B  <xsl:accumulator-rule match="block"
B B B  select="let $line-width-so-far := $value[1],
B B B B B B B B B B B B B B B  $current-width := xs:integer((@width, 100)[1]),
B B B B B B B B B B B B B B B  $would-be-line-width := $line-width-so-far
B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B  + $current-width
B B B B B B B B B B B B B B B  return (
B B B B B B B B B B B B B B B B B  if ($would-be-line-width gt 100)
B B B B B B B B B B B B B B B B B B B  then $current-width
B B B B B B B B B B B B B B B B B B B  else (),
B B B B B B B B B B B B B B B B B  $would-be-line-width
B B B B B B B B B B B B B B B  )"/>
</xsl:accumulator>

If the line becomes too long, the width of the current block will be prepended to the would-be line length in the accumulator. So then there is a sequence of "line length if line wrap happened before current block" and "line length if line wrap did not happen before current block".
If, on the other hand, the would-be line length is less than or equal to 100, only a single value (the would-be line length) will go into the accumulator.


I added its accumulator value(s) to each block:

<blocks>
B B  <block id="i1" width="100" acc="100">content</block>
B B  <block type="composite">
B B B B B  <block id="i2" width="33" acc="33 133">content</block>
B B B B B  <block id="i3" width="67" acc="100">content</block>
B B  </block>
B B  <block type="composite">
B B B B B  <block id="i4" width="50" acc="50 150">content</block>
B B B B B  <block id="i5" width="50" acc="100">content</block>
B B  </block>
B B  <block id="i6" width="25" acc="25 125">content</block>
B B  <block id="i6a" width="80" acc="80 105">content</block>
B B  <block width="100" acc="100 180">content</block>
B B  <block type="composite">
B B B B B  <block id="i7" width="55" acc="55 155">content</block>
B B B B B  <block id="i8" width="20" acc="75">content</block>
B B  </block>
B B  <block id="i9" width="100" acc="100 175">content</block>
</blocks>

This has been achieved with a single grouping:

<xsl:template match="blocks">
B  <xsl:copy>
B B B  <xsl:for-each-group select="block"
group-starting-with="block[accumulator-before('width')[last()] gt 100]">
B B B B B  <xsl:choose>
B B B B B B B  <xsl:when test="count(current-group()) eq 1">
B B B B B B B B B  <xsl:apply-templates select="current-group()"/>
B B B B B B B  </xsl:when>
B B B B B B B  <xsl:otherwise>
B B B B B B B B B  <block type="composite">
B B B B B B B B B B B  <xsl:apply-templates select="current-group()"/>
B B B B B B B B B  </block>
B B B B B B B  </xsl:otherwise>
B B B B B  </xsl:choose>
B B B  </xsl:for-each-group>
B  </xsl:copy>
</xsl:template>

Gerrit


On 17.09.2020 00:16, Martin Honnen martin.honnen@xxxxxx wrote:

I was wondering what output is wanted for


<blocks>
B B  <block id="i1">content</block>
B B  <block id="i2" width="33">content</block>
B B  <block id="i3" width="67">content</block>
B B  <block id="i4" width="50">content</block>
B B  <block id="i5" width="50">content</block>
B B  <block id="i6" width="25">content</block>
B B  <block>content</block>
B B  <block id="i7" width="55">content</block>
B B  <block id="i8" width="20">content</block>
B B  <block id="i9">content</block>
</blocks>

Neither my suggestion nor the above seem to only wrap the adjacent
block[@width] where the accumulator gives 100.

I complicated or strengthened my attempt as

B B  <xsl:template match="blocks">
B B B B  <xsl:copy>
B B B B B B  <xsl:for-each-group select="block" group-adjacent="not(@width)">
B B B B B B B B  <xsl:choose>
B B B B B B B B B B  <xsl:when test="current-grouping-key()">
B B B B B B B B B B B B  <xsl:apply-templates select="current-group()"/>
B B B B B B B B B B  </xsl:when>
B B B B B B B B B B  <xsl:otherwise>
B B B B B B B B B B B B  <xsl:for-each-group select="current-group()"
group-ending-with="block[accumulator-before('width') eq 100]">
B B B B B B B B B B B B B B  <xsl:choose>
B B B B B B B B B B B B B B B B  <xsl:when
test="current-group()[last()][accumulator-before('width') eq 100]">
B B B B B B B B B B B B B B B B B B  <block type="composite">
B B B B B B B B B B B B B B B B B B B B  <xsl:apply-templates select="current-group()"/>
B B B B B B B B B B B B B B B B B B  </block>
B B B B B B B B B B B B B B B B  </xsl:when>
B B B B B B B B B B B B B B B B  <xsl:otherwise>
B B B B B B B B B B B B B B B B B B  <xsl:apply-templates select="current-group()"/>
B B B B B B B B B B B B B B B B  </xsl:otherwise>
B B B B B B B B B B B B B B  </xsl:choose>
B B B B B B B B B B B B  </xsl:for-each-group>
B B B B B B B B B B  </xsl:otherwise>
B B B B B B B B  </xsl:choose>
B B B B B B  </xsl:for-each-group>
B B B B  </xsl:copy>
B B  </xsl:template>

Current Thread