Re: [xsl] XSLT2: Grouping mixed content between opening and closing marker elements

Subject: Re: [xsl] XSLT2: Grouping mixed content between opening and closing marker elements
From: "Keith Burt keithburt66@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Sun, 16 Aug 2020 19:50:00 -0000
Martin, thank you for your response.

I am not familiar with V3 but will challenge myself to understand your code.

Regards.

--
Keith


On Saturday, August 15, 2020, Martin Honnen martin.honnen@xxxxxx <
xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:

> Am 15.08.2020 um 13:56 schrieb Martin Honnen martin.honnen@xxxxxx:
>
>> Am 15.08.2020 um 12:19 schrieb Keith Burt keithburt66@xxxxxxxxx:
>>
>> In my simplified example, I have self closing 'e' elements that have a
>>> tag attribute that denotes the start and end points of where style (in
>>> this case bold and italic) should be applied.
>>> I want to group the content within the start and end markers, and create
>>> a wrapper element based on the tag attribute.
>>>
>>
>> That sounds like a grouping task for a nested
>>    for-each-group group-starting-with="e"
>>      for-each-group group-ending-with="e"
>> with some more precise conditions.
>>
>> Or in XSLT 3 you could think of xsl:iterate or fold-left.
>>
>
> Looking at it closer it seems you could use the for-each-group within an
> iterate, to handle the nesting of e.g. <e tag="b">...<e tag="i">...<e
> tag="/i">...<e tag="/i>".
>
>
> <?xml version="1.0" encoding="utf-8"?>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
>   version="3.0"
>   xmlns:xs="http://www.w3.org/2001/XMLSchema";
>   exclude-result-prefixes="#all"
>   xmlns:mf="http://example.com/mf";
>   expand-text="yes">
>
>   <xsl:function name="mf:wrap" as="node()*">
>     <xsl:param name="nodes" as="node()*"/>
>     <xsl:param name="tag" as="xs:string"/>
>     <xsl:for-each-group select="$nodes" group-starting-with="e[@tag =
> $tag]">
>       <xsl:choose>
>         <xsl:when test="not(self::e[@tag = $tag])">
>           <xsl:apply-templates select="current-group()"/>
>         </xsl:when>
>         <xsl:otherwise>
>           <xsl:for-each-group select="tail(current-group())"
> group-ending-with="e[@tag = '/' || $tag]">
>             <xsl:choose>
>               <xsl:when test="current-group()[last()][self::e][@tag =
> '/' || $tag]">
>                 <xsl:element name="{$tag}">
>                   <xsl:apply-templates
> select="current-group()[position() lt last()]"/>
>                 </xsl:element>
>               </xsl:when>
>               <xsl:otherwise>
>                 <xsl:apply-templates select="current-group()"/>
>               </xsl:otherwise>
>             </xsl:choose>
>           </xsl:for-each-group>
>         </xsl:otherwise>
>       </xsl:choose>
>     </xsl:for-each-group>
>   </xsl:function>
>
>   <xsl:function name="mf:wrap" as="node()*">
>     <xsl:param name="nodes" as="node()*"/>
>     <xsl:iterate
> select="distinct-values($nodes[self::e/@tag[not(starts-with(.,
> '/'))]]/@tag)">
>       <xsl:param name="nodes" as="node()*" select="$nodes"/>
>       <xsl:on-completion>
>         <xsl:sequence select="$nodes"/>
>       </xsl:on-completion>
>       <xsl:next-iteration>
>         <xsl:with-param name="nodes" select="mf:wrap($nodes, .)"/>
>       </xsl:next-iteration>
>     </xsl:iterate>
>   </xsl:function>
>
>   <xsl:mode on-no-match="shallow-copy"/>
>
>   <xsl:template match="*[e[@tag]]">
>     <xsl:copy>
>       <xsl:apply-templates select="@*"/>
>       <xsl:sequence select="mf:wrap(node())"/>
>     </xsl:copy>
>   </xsl:template>
>
> </xsl:stylesheet>
>
>
> That's XSLT 3 obviously, so I am not sure whether it helps as your
> original question only mentioned XSLT 2. However, since 2017, we have
> XSLT 3 and Saxon 9.8 and later or Altova XML 2017 R3 and later should
> support XSLT 3.

Current Thread