xsl-list
[Top] [All Lists]

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

2020-08-16 14:50:07
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(_dot_)honnen(_at_)gmx(_dot_)de <
xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com> wrote:

Am 15.08.2020 um 13:56 schrieb Martin Honnen 
martin(_dot_)honnen(_at_)gmx(_dot_)de:

Am 15.08.2020 um 12:19 schrieb Keith Burt keithburt66(_at_)gmail(_dot_)com:

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.


--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com
--~--
<Prev in Thread] Current Thread [Next in Thread>