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