xsl-list
[Top] [All Lists]

Re: [xsl] Feedback on grouping solution

2019-10-28 07:52:25
Hi Dimitre,

 

Thanks for the follow up. Yes, the children of adjacent step elements have to 
be treated as one group and that lead me to discover another possibility: the 
first para in each step in the group has to start on the "left" (an 
odd-numbered position in the output group). I modified the test input so that 
the first para in the last step would require a spacer.

 

<?xml version="1.0" encoding="UTF-8"?>

<procedure>

    <title/>

    <step>

        <para/>

        <important/>

        <figure/>

        <figure/>

    </step>

    <note/>

    <note/>

    <step>

        <para/>

        <figure/>

        <figure/>

        <note/>

        <figure/>

        <caution/>

    </step>

    <step>

        <para/> <!-- will need a spacer -->

        <figure/>

        <figure/>

    </step>    

</procedure>

 

Here is the updated stylesheet:

 

<?xml version="1.0" encoding="UTF-8"?>

<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:output indent="yes"/>

    

    <xsl:template match="/procedure">

        <procedure>

            <!-- Group the children of the procedure, keeping adjacent steps 
together. -->

            <xsl:for-each-group select="child::*" 
group-adjacent="boolean(self::step)">

                <xsl:choose>

                    <xsl:when test="current-grouping-key()">

                        <!-- step group, process the children. -->

                        <xsl:variable name="elements" 
select="current-group()/*"/>

                        <xsl:call-template name="step-element">

                            <xsl:with-param name="elements" select="$elements"/>

                            <xsl:with-param name="positionInGroup" select="1"/>

                            <xsl:with-param name="positionInOutput" select="1"/>

                        </xsl:call-template>

                    </xsl:when>

                    <xsl:otherwise>

                        <xsl:apply-templates select="current-group()"/>

                    </xsl:otherwise>

                </xsl:choose>

            </xsl:for-each-group>

        </procedure>

    </xsl:template>

    

    <xsl:template name="step-element">

        <xsl:param name="elements"/>

        <xsl:param name="positionInGroup"/>

        <xsl:param name="positionInOutput"/>

        <xsl:variable name="currentElement" 
select="$elements[$positionInGroup]"/>

        <xsl:choose>

            <xsl:when test="$currentElement[self::figure or self::para]">

                <xsl:choose>

                    <!-- See where the figure or first para is going to fall in 
the output. -->

                    <xsl:when test="($currentElement[self::figure] and 
$positionInOutput mod 2 != 0) or 

                        ($currentElement[self::para] and $positionInOutput mod 
2 = 0 and not($currentElement/preceding-sibling::*))">

                        <!-- A spacer to force the figure to the right

                             or the first para in a step to the left. -->

                        <spacer/>  <xsl:comment select="$positionInOutput"/>

                        <xsl:call-template name="step-element">

                            <xsl:with-param name="elements" select="$elements"/>

                            <xsl:with-param name="positionInGroup" 
select="$positionInGroup"/>

                            <xsl:with-param name="positionInOutput" 
select="$positionInOutput + 1"/>

                        </xsl:call-template>

                    </xsl:when>

                    <xsl:otherwise>

                        <xsl:apply-templates select="$currentElement"/> 
<xsl:comment select="$positionInOutput"/>

                        <xsl:if test="$elements[$positionInGroup + 1]">

                            <xsl:call-template name="step-element">

                                <xsl:with-param name="elements" 
select="$elements"/>

                                <xsl:with-param name="positionInGroup" 
select="$positionInGroup + 1"/>

                                <xsl:with-param name="positionInOutput" 
select="$positionInOutput + 1"/>

                            </xsl:call-template>

                        </xsl:if>

                    </xsl:otherwise>

                </xsl:choose>

            </xsl:when>

            <xsl:otherwise>

                <xsl:apply-templates select="$currentElement"/> <xsl:comment 
select="$positionInOutput"/>

                <xsl:if test="$elements[$positionInGroup + 1]">

                    <xsl:call-template name="step-element">

                        <xsl:with-param name="elements" select="$elements"/>

                        <xsl:with-param name="positionInGroup" 
select="$positionInGroup + 1"/>

                        <xsl:with-param name="positionInOutput" 
select="$positionInOutput + 1"/>

                    </xsl:call-template>

                </xsl:if>

            </xsl:otherwise>

        </xsl:choose>

    </xsl:template>

    

    <!-- Identity transform -->

    <xsl:template match="element()">

        <xsl:copy>

            <xsl:apply-templates select="@*, node()"/>

        </xsl:copy>

    </xsl:template>

    

</xsl:stylesheet>

 

Here is the output with comments indicating each element's position within the 
group in the output.

 

<?xml version="1.0" encoding="UTF-8"?>

<procedure>

   <title/>

   <para/>   <!--1-->

   <important/>   <!--2-->

   <spacer/>   <!--3-->

   <figure/>   <!--4-->

   <spacer/>   <!--5-->

   <figure/>   <!--6-->

   <note/>

   <note/>

   <para/>   <!--1-->

   <figure/>   <!--2-->

   <spacer/>   <!--3-->

   <figure/>   <!--4-->

   <note/>   <!--5-->

   <figure/>   <!--6-->

   <caution/>   <!--7-->

   <spacer/>   <!--8-->

   <para/>   <!--9-->

   <figure/>   <!--10-->

   <spacer/>   <!--11-->

   <figure/>   <!--12-->

</procedure>

 

Although the two-pass solution is more compact and XSLT 1.0-compatible, it has 
been useful for me to work through and try to understand the recursion I have 
to use. Thank you to all who contributed. This is a great list!

 

Rick

 
--~----------------------------------------------------------------
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>