Now waiting for Dimitre posting a less "fancy"
but equally compact XSLT 1 solution :)
The following is a 48-lines XSLT 2.0 solution (2 passes -- equivalent XSLT
1.0 solution is easy to produce but will need the vendor:node-set()
extension function).
Martin's XSLT 3.0 solution is 43 lines when <xsl:stylesheet> and
<xsl:output> are added. So the difference in number of lines is not big --
12% and I believe the XSLT 2 solution below is less complex and more easily
understandable.
Maybe I am biased, but I personally believe that it is better to use the
standard XPath 3 functions fold-left() and fold-right() in the spirit of
functional programming. <xsl:iterate> instead helps people avoid thinking
using the concepts of functional programming. Also, its definition is so
complex that I personally needed more than an hour to find out / construct
what cases of its usage are possible and which are mutually exclusive.
Here is the transformation:
<xsl:stylesheet version="2.0" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vPass1Result"><xsl:apply-templates
select="/*"/></xsl:variable>
<xsl:template match="node()|@*" mode="#default pass2">
<xsl:copy>
<xsl:apply-templates select="node()|(@* except @stepChild)"
mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="$vPass1Result/*" mode="pass2"/>
</xsl:template>
<xsl:template match="step"><xsl:apply-templates/></xsl:template>
<xsl:template match="step/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:attribute name="stepChild">
<xsl:number level="any" count="/*/step/*"/>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*" mode="pass2">
<xsl:copy>
<xsl:apply-templates select="*[1]" mode="pass2"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/*" mode="pass2">
<xsl:param name="pAdjustments" select="0"/>
<xsl:variable name="vNeedsAdjustment" select="self::figure and
(count(preceding-sibling::*[@stepChild]) +1 + $pAdjustments) mod 2 = 1"/>
<xsl:if test="$vNeedsAdjustment">
<spacer/>
</xsl:if>
<xsl:next-match/>
<xsl:apply-templates select="following-sibling::*[1]" mode="pass2">
<xsl:with-param name="pAdjustments" select="$pAdjustments +
(if($vNeedsAdjustment) then 1 else 0)"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
Cheers,
Dimitre
On Sat, Oct 26, 2019 at 12:06 PM Martin Honnen
martin(_dot_)honnen(_at_)gmx(_dot_)de <
xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com> wrote:
On 26.10.2019 19:03, Rick Quatro rick(_at_)rickquatro(_dot_)com wrote:
I need to process the <step> child elements so that the <figure>
elements are always on the "right" (even-numbered position) in the
output. Immediate children of the <procedure> do not factor into the
odd/even sequence.
The first child of each group of adjacent <step> elements starts a new
odd/even series. To ensure that the each <figure> is in an even-numbered
position, I want to insert a <spacer> element where it is required.
Here is my stylesheet. My basic question is: is there a better or more
efficient way to do this?
I think with XSLT 3 it is possible to use xsl:iterate on the child
elements of the adjacent steps found by grouping:
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="procedure">
<xsl:copy>
<xsl:for-each-group select="*"
group-adjacent="boolean(self::step)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:iterate select="current-group()/*">
<xsl:param name="position-in-output"
select="1"/>
<xsl:apply-templates select=".">
<xsl:with-param
name="position-in-output" select="$position-in-output"/>
</xsl:apply-templates>
<xsl:next-iteration>
<xsl:with-param name="position-in-output"
select="if (self::figure and
$position-in-output mod 2 = 1)
then $position-in-output + 2
else $position-in-output +
1"/>
</xsl:next-iteration>
</xsl:iterate>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="step/figure">
<xsl:param name="position-in-output"/>
<xsl:if test="$position-in-output mod 2 = 1">
<spacer/>
</xsl:if>
<xsl:next-match/>
</xsl:template>
Now waiting for Dimitre posting a less "fancy" but equally compact XSLT
1 solution :)
--
Cheers,
Dimitre Novatchev
---------------------------------------
Truly great madness cannot be achieved without significant intelligence.
---------------------------------------
To invent, you need a good imagination and a pile of junk
-------------------------------------
Never fight an inanimate object
-------------------------------------
To avoid situations in which you might make mistakes may be the
biggest mistake of all
------------------------------------
Quality means doing it right when no one is looking.
-------------------------------------
You've achieved success in your field when you don't know whether what
you're doing is work or play
-------------------------------------
To achieve the impossible dream, try going to sleep.
-------------------------------------
Facts do not cease to exist because they are ignored.
-------------------------------------
Typing monkeys will write all Shakespeare's works in 200yrs.Will they write
all patents, too? :)
-------------------------------------
Sanity is madness put to good use.
-------------------------------------
I finally figured out the only reason to be alive is to enjoy it.
--~----------------------------------------------------------------
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
--~--