Search code examples
xsltxslt-2.0xslt-grouping

xsl:for-each-group does not give me group boundaries included in the groups


I am transforming mathematical formulas from one proprietary format to another. The main problem is that the original format lists elements in the order in which they appear, where the target format uses a logical definition along the lines of Strict Content MathML (but not at all the same - not even XML in this case).

The following formula:

<FORMULA>
  <EXPR>A</EXPR>
  <OP.CMP TYPE="EQ"/>
  <EXPR>B</EXPR>
  <OP.CMP TYPE="EQ"/>
  <EXPR>C</EXPR>
</FORMULA>

Would become:

equal[char[A],char[B],char[C]]

This is fairly simple, but when the TYPE of the second OP.CMP differs from the first it changes:

<FORMULA>
  <EXPR>A</EXPR>
  <OP.CMP TYPE="EQ"/>
  <EXPR>B</EXPR>
  <OP.CMP TYPE="NE"/>
  <EXPR>C</EXPR>
</FORMULA>

Becomes:

equal[char[A],notequal[char[B],char[C]]]

As there are many nested levels inside each I need to process the formula by first grouping the elements using the operators OP.CMP and OP.MATH as group boundaries. Then I would pick up the type of the OP.CMP or OP.MATH to start the group. But when I use xml:for-each-group ending on the OP.CMP or OP.MATH, I am not finding the OP.CMP or OP.MATH included in the group, which I would have expected. Here is my simple test XSL:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

<xsl:template match="FORMULA" mode="eqn">
    <xsl:for-each-group select="node()" group-ending-with="OP.CMP">
        <xsl:message>Group <GRP><xsl:copy-of select="."/></GRP></xsl:message>
    </xsl:for-each-group>
</xsl:template>

What I am getting is this:

Group <GRP><EXPR>A</EXPR></GRP>

Group <GRP><EXPR>B</EXPR></GRP>

Group <GRP><EXPR>C</EXPR></GRP>

where I was expecting this:

Group <GRP><EXPR>A</EXPR><OP.CMP TYPE="EQ"/></GRP>

Group <GRP><EXPR>B</EXPR><OP.CMP TYPE="NE"/></GRP>

Group <GRP><EXPR>C</EXPR></GRP>

Is the element that ends the group not included in that group? If so, is there something else I should use instead?


Solution

  • Inside of for-each-group the context item . is the first item in each group, not the complete group; the complete group you can access with current-group(), in the case of group-ending-with the item current-group()[last()] would be the matched item (or the last item left).