Search code examples
xmlxsltxslt-2.0

How to keep track of position from a template to a different template?


I would like to represent the following XML in svg, but I'm currently facing issues with keeping track

of the current position when going from a <simplePath> to a <jump>

Within a simplePath, the distance between a point is 50

From a simplePath to a Jump and vice versa, the distance is 200


<root>
        <simplePath>
            <point>A</point>
            <point>B</point>
            <point>C</point>
        </simplePath>

        <jump>
            <simplePath>
                <point>D</point>
                <point>E</point>
                <point>F</point>
             </simplePath>
        </jump>

        <simplePath>
            <point>G</point>
        </simplePath>

</root>

The output of the XML should be :

A : 0
B : 50
C : 100

D : 300
E : 350
F : 400

G : 600

When it's mainly composed of simplePath, I have no issue doing it by using ((position() -1) * 50)

I can't figure how do it with a <jump>

    <xsl:template match="root">
        <xsl:apply-templates select="simplePath | jump"/>
    </xsl:template>

    <xsl:template match="simplePath">
        <xsl:apply-templates select="point"/>
    </xsl:template>

    <xsl:template match="point">
        <xsl:value-of select="."/>
        <xsl:value-of select="(position() - 1)* 50"/>
        <xsl:text>&#xa;</xsl:text>
    </xsl:template>

    <xsl:template match="jump">
        <xsl:apply-templates select="simplePath"/>
    </xsl:template>

output :

A0
B50
C100
D0
E50
F100
G0

Solution

  • Here is a relatively simple way you could look at it:

    XSLT 2.0

    <xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" encoding="UTF-8" />
    
    <xsl:template match="/root">
        <xsl:call-template name="process">
            <xsl:with-param name="points" select=".//point"/>
        </xsl:call-template>
    </xsl:template>
    
    <xsl:template name="process">
        <xsl:param name="points" />
        <xsl:param name="total" select="0"/>
        <!-- output -->
        <xsl:value-of select="$points[1]"/>
        <xsl:text> : </xsl:text>
        <xsl:value-of select="$total"/>
        <!-- recursive call -->
        <xsl:if test="count($points) > 1">
            <xsl:text>&#10;</xsl:text>
            <xsl:call-template name="process">
                <xsl:with-param name="points" select="$points[position() > 1]"/>
                <xsl:with-param name="total" select="if(boolean($points[1]/ancestor::jump) != boolean($points[2]/ancestor::jump)) then $total + 200 else $total + 50"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Applied to your input example, this will return:

    Result

    A : 0
    B : 50
    C : 100
    D : 300
    E : 350
    F : 400
    G : 600