Home > Mobile >  How to keep track of position from a template to a different template?
How to keep track of position from a template to a different template?

Time:03-28

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

CodePudding user response:

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

CodePudding user response:

Here is how you could do it, if I understand the requirement correctly, using XSLT 3 (e.g. Saxon 9.8 or later, Saxon-JS 2, Altova XML 2017 R3 or later) and an accumulator:

<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"
                expand-text="yes">
  
  <xsl:accumulator name="distance" as="item()*" initial-value="0, ()">
    <xsl:accumulator-rule match="point" select="if ($value[2] = 'point') then ($value[1]   50, local-name()) else if ($value[2] = 'jump') then ($value[1]   200, 'point') else ($value[1], local-name())"/>
    <xsl:accumulator-rule match="jump" select="$value[1], local-name()"/>
    <xsl:accumulator-rule match="jump" phase="end" select="$value[1], local-name()"/>
  </xsl:accumulator>
  
  <xsl:template match="point">
    <xsl:comment>accumulator-before('distance') : {accumulator-before('distance')[1]}</xsl:comment>
    <xsl:next-match/>
  </xsl:template>

  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:mode on-no-match="shallow-copy" use-accumulators="distance"/>

</xsl:stylesheet>

Output for your sample:

<root>
   <simplePath><!--accumulator-before('distance') : 0-->
      <point>A</point>
      <!--accumulator-before('distance') : 50-->
      <point>B</point>
      <!--accumulator-before('distance') : 100-->
      <point>C</point>
   </simplePath>
   <jump>
      <simplePath><!--accumulator-before('distance') : 300-->
         <point>D</point>
         <!--accumulator-before('distance') : 350-->
         <point>E</point>
         <!--accumulator-before('distance') : 400-->
         <point>F</point>
      </simplePath>
   </jump>
   <simplePath><!--accumulator-before('distance') : 600-->
      <point>G</point>
   </simplePath>
</root>
  • Related