Substring value Processing in recursion - xslt

This is all about reverse a string. It works properly for the given Value 'ABCDEF'. The output is also correct 'FEDCBA'. But I want to know how this is printing letters 'A' and 'D' in this string. Could anyone help me to understand this? please.
Elaborate this working method.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:output method="html"/>
<xsl:template name="reverse">
<xsl:param name="input" select="'ABCDEF'"/>
<xsl:variable name="len" select="string-length($input)"/>
<xsl:choose>
<xsl:when test="$len < 2">
<xsl:value-of select="$input"/>
</xsl:when>
<xsl:when test="$len = 2">
<xsl:value-of select="substring($input,2,1)"/>
<xsl:value-of select="substring($input,1,1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="mid" select="floor($len div 2)"/>
<xsl:call-template name="reverse">
<xsl:with-param name="input" select="substring($input,$mid+1,$mid+1)"/>
</xsl:call-template>
<xsl:call-template name="reverse">
<xsl:with-param name="input" select="substring($input,1,$mid)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="reverse">
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>

Here's what happens:
Input ABCDEF
len = 6
len is > 2 and not = 2
mid = 3
Call again with input DEF
len = 3
len is > 2 and not = 2
mid = 1
Call again with input EF
len = 2
len is not > 2 but = 2
Put out the second letter F
Put out the first letter E
Return to outer call
Call with input = D
len is less than 2
Output D
Return to outer call
Call again with input = ABC
len = 3
len is > 2 and not 2
mid = 1
Call again with input BC
len = 2
len is not > 2 but = 2
Put out the second letter C
Put out the first letter B
Return to outer call
Call with input A
len is less than 2
Output A

Related

How to keep track of position from a template to a different template (2)?

Follow on question to: https://stackoverflow.com/questions/71635891/how-to-keep-track-of-position-from-a-template-to-a-different-template?noredirect=1#comment126614972_71635891
My new problem is, there could be more than one simplePath in a jump and I want all simplePath inside a jump to start
from the same position. My current is issue is that I'm not competent enough to translate my current idea into XSL, I added in comment in the XSLT code what I'm trying to achieve
D --- E --- F K --- L --- M --- N --- O
/ \ /
A --- B --- C--- / \ -- J ---/
\ / \
\ / \
G --- H --- I Q --- R --- S
<root>
<simplePath>
<point>A</point>
<point>B</point>
<point>C</point>
</simplePath>
<jump>
<simplePath>
<point>D</point>
<point>E</point>
<point>F</point>
</simplePath>
<simplePath>
<point>G</point>
<point>H</point>
<point>I</point>
</simplePath>
</jump>
<simplePath>
<point>J</point>
</simplePath>
</root>
expected output
A : 0
B : 50
C : 100
D : 300
E : 350
F : 400
Dbis : 300
Ebis : 350
Fbis : 400
G : 600
poor attempt :
<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"/>
<xsl:param name="savePos" select="0"/>
<!-- new param -->
<!-- output -->
<xsl:value-of select="boolean($points[1]/ancestor::jump)"/>
<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"/>
<!-- my "attempt" -->
<!-- I want to save the pos of total IF it's the first point of the first simplePath in a Jump -->
<xsl:with-param name="savePos" select=""/>
<!-- Next, I will add a new "if condition" here
<xsl:with-param name="total" select="if(boolean($points[1]/ancestor::jump) != boolean($points[2]/ancestor::jump))
then
>>>>if I'm not in the first simplePath of "jump", and it's the first "point" of this "simplePath" then
$total = $savePos
else
$total + 200
else
$total + 50"/>
- -->
</xsl:call-template>
</xsl:if>
</xsl:template>

Multiplying two variables in XSLT-1

I have tried for hours to multiply 2 varibales in XSLT1 but I can't seem to figure it out.
The result shows in excel as NaN
I have searched for a while but I'm having trouble with this, just started using XSLT
Variables:
Weight = 10
Length = 12
XSLT:
<xsl:variable name ="content"><xsl:value-of select="$length * $weight" /></xsl:variable>
EDIT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rvmon="rosenbergverft.com:3D/2017/WeightMTO" xmlns:pdms="rosenbergverft.com:3D/2010/Models">
<xsl:output method="text" omit-xml-declaration="yes" standalone="no" media-type="text/xls" />
<xsl:decimal-format name="euro" decimal-separator="," grouping-separator="." />
<xsl:template match="/">Building block;Discipline;Area;Sub. Area;Syst. no.;Weight Item no.;Piecemark no.;Weight Item Descr.;CompType;Type code;Rating or Scedule;Spec.;Dim. (");Material;Cost Code COR;Weight Phase Code;Mod. Phase Code;Weight Status % (allow.);Mod. Constr. Code;Mod. Handl. Code;No. off;Unit Length/ Area (m,m2, m3);Unit Weight (kg/m, m2, m3);Unit Weight MON(kg/m, m2, m3);Net Dry Weight (kg);Gross Dry Weight (kg);Content Weight (kg);Installed weight;CoG East;CoG North;CoG Elevation;Ref. doc., dwg., P&ID etc.;Remarks;Cost for instrument valves [NOK];install code;MON Lift Code;Paint Surface;Insulation Class;
<xsl:for-each select="/rvmon:WeightMTO/rvmon:MTOOwner"><xsl:for-each select="rvmon:Piecemark/rvmon:WeightItemNo/rvmon:Part">
<xsl:variable name= "weight" select="format-number(rvmon:ContentWeight/#number, '#0,0##', 'euro')" />
<xsl:variable name="length"><xsl:choose><xsl:when test="contains(rvmon:CompType,'BOLT')"><xsl:text> 1 </xsl:text></xsl:when><xsl:when test="rvmon:UnitLength/#unit = 'mm'"><xsl:value-of select="format-number(rvmon:UnitLength/#number div 1000, '#0,0##', 'euro')" /></xsl:when><xsl:when test="rvmon:UnitLength/#unit = 'mm2'"><xsl:value-of select="format-number(rvmon:UnitLength/#number div 1000000, '#0,0##', 'euro')" /></xsl:when><xsl:when test="rvmon:UnitLength/#unit = 'mm3'"><xsl:value-of select="format-number(rvmon:UnitLength/#number div 1000000000, '#0,0##', 'euro')" /></xsl:when><xsl:otherwise><xsl:value-of select="format-number(rvmon:UnitLength/#number, '#0,0##', 'euro')" /></xsl:otherwise></xsl:choose></xsl:variable>
<xsl:variable name="content" select="$length * $weight" />
<xsl:value-of select="../../../rvmon:BuildingBlock" />;<xsl:value-of select="rvmon:Discipline" />;<xsl:value-of select="../../../rvmon:Module" />;<xsl:value-of select="../../../rvmon:SubArea" />;<xsl:value-of select="../../../rvmon:SystNo" />;<xsl:value-of select="rvmon:WeightItemNo" />;<xsl:value-of select="rvmon:Piecemark" />;<xsl:value-of select="rvmon:WeightItemDescr" />;<xsl:value-of select="rvmon:CompType" />;<xsl:value-of select="rvmon:TypeCode" />;<xsl:value-of select="rvmon:Rating" />;<xsl:value-of select="rvmon:Spec" />;<xsl:choose><xsl:when test="rvmon:Discipline = 'L'"><xsl:value-of select="format-number(rvmon:Dim/#number, '#0,##', 'euro')" /><xsl:choose><xsl:when test="rvmon:Dim/#unit = 'inch'">"</xsl:when><xsl:otherwise><xsl:value-of select="rvmon:Dim/#unit" /></xsl:otherwise></xsl:choose></xsl:when><xsl:when test="rvmon:Discipline = 'N'"></xsl:when><xsl:otherwise><xsl:value-of select="format-number(rvmon:Dim/#number, '#0,##', 'euro')" /><xsl:value-of select="rvmon:Dim/#unit" /></xsl:otherwise></xsl:choose>;<xsl:value-of select="rvmon:Material" />;<xsl:value-of select="rvmon:CostCodeCOR" />;<xsl:value-of select="rvmon:WeightPhaseCode" />;<xsl:value-of select="rvmon:ModPhaseCode" />;<xsl:value-of select="format-number(rvmon:WeightStatus/#number * 100, '00', 'euro')" />%;<xsl:value-of select="rvmon:ModConstrCode" />;<xsl:value-of select="rvmon:ModHandlCode" />;<xsl:value-of select="format-number(rvmon:NoOff/#number, '#0,###', 'euro')" />;<xsl:value-of select="$length"/>;<xsl:if test="rvmon:ModHandlCode = 'RP'">-</xsl:if><xsl:value-of select="format-number(rvmon:UnitWeight/#number, '#0,0##', 'euro')" />;<xsl:if test="rvmon:ModHandlCode = 'RP'">-</xsl:if><xsl:value-of select="format-number(rvmon:MONUnitWeightFactor/#number, '#0,0#####', 'euro')" />;<xsl:choose><xsl:when test="contains(rvmon:CompType,'BOLT')"><xsl:text> 1 </xsl:text></xsl:when><xsl:otherwise><xsl:value-of select="format-number(rvmon:NetDryWeight/#number, '#0,0##', 'euro')" /></xsl:otherwise></xsl:choose>;<xsl:value-of select="format-number(rvmon:AllowanceWeight/#number, '#0,0##', 'euro')" />;<xsl:if test="rvmon:ModHandlCode = 'RP'">-</xsl:if><xsl:value-of select="$weight"/>;<xsl:if test="rvmon:ProdStatus ='ABC' "><xsl:text> Yes </xsl:text></xsl:if>;<xsl:value-of select="format-number(rvmon:CoGEast/#number div 1000, '#0,0##', 'euro')" />;<xsl:value-of select="format-number(rvmon:CoGNorth/#number div 1000, '#0,0##', 'euro')" />;<xsl:value-of select="format-number(rvmon:CoGElevation/#number div 1000, '#0,0##', 'euro')" />;<xsl:value-of select="../../../rvmon:DrawNumber" />;<!-- xsl:value-of select="rvmon:Remarks" / --><xsl:value-of select="../../../pdms:PDMSElement/pdms:ElementName/#text" />;;;;<xsl:value-of select="format-number(rvmon:PaintSurface/#number div 1000000, '#0,0##', 'euro')" />;<xsl:value-of select="../../../rvmon:InsulationClass" />;<xsl:value-of select="$length"/>;<xsl:value-of select="$content"/><xsl:text>
</xsl:text></xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
XML-document
<UnitLength number="1.000" unit="pcs" field="Quantity" />
<ContentWeight number="8.005" unit="kg" field="ContentWeight" /> />
I am able to output "weight" and "length" by itself. But I cant even do
"weight * 10"
Sorry if it is messy.
You need to remove all formatting number while assigning the variable and while multipled result need to format number like below:
<?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"
xmlns:rvmon="rosenbergverft.com:3D/2017/WeightMTO"
exclude-result-prefixes="#all"
version="1.0">
<xsl:decimal-format name="euro" decimal-separator="," grouping-separator="." />
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<xsl:variable name= "weight" select="//rvmon:ContentWeight/#number" />
<xsl:variable name="length">
<xsl:choose>
<xsl:when test="contains(//rvmon:CompType,'BOLT')">
<xsl:text>1</xsl:text>
</xsl:when>
<xsl:when test="rvmon:UnitLength/#unit = 'mm'">
<xsl:value-of select="//rvmon:UnitLength/#number div 1000" />
</xsl:when>
<xsl:when test="rvmon:UnitLength/#unit = 'mm2'">
<xsl:value-of select="//rvmon:UnitLength/#number div 1000000" />
</xsl:when>
<xsl:when test="rvmon:UnitLength/#unit = 'mm3'">
<xsl:value-of select="//rvmon:UnitLength/#number div 1000000000" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//rvmon:UnitLength/#number" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="content" select="format-number(($weight * $length), '#0,0##', 'euro')" />
<xsl:value-of select="$content"/>
</xsl:template>
</xsl:stylesheet>
You have:
<xsl:variable name= "weight" select="format-number(rvmon:ContentWeight/#number, '#0,0##', 'euro')" />
A number formatted to use a decimal comma is no longer a number for the purposes of XPath/XSLT. A number can contain only digits and a decimal period.
Assuming that the original values (not shown in your question) are true numbers, use them for the multiplication, and format them only for the output.

How to do this in XSLT using version 1.0

A list of numbers corresponding to the factors is defined with this string : 24850973612485097361
The number is flipped and is multiplied for each number with the factor corresponding to its position, 0 corresponding to 10 and all cumulated
For example, for and order having the number 28200703:
The number is flipped giving 30700282
Using the factor string 24850973612485097361 for the corresponding size for the number, the calculation is done as following:
3 x 2 = 6
0 x 4 = 0
7 x 8 = 56
0 x 5 = 0
0 x 10 = 0
2 x 9 = 18
8 x 7 = 56
2 x 3 = 6
Cumulated: 142
Try it this way:
<xsl:template name="process">
<xsl:param name="number"/>
<xsl:param name="factors" select="'24850973612485097361'"/>
<xsl:param name="accumulated" select="0"/>
<xsl:choose>
<xsl:when test="$number">
<xsl:variable name="n" select="$number mod 10" />
<xsl:variable name="m" select="substring($factors, 1, 1)" />
<xsl:call-template name="process">
<xsl:with-param name="number" select="floor($number div 10)"/>
<xsl:with-param name="factors" select="substring($factors, 2)"/>
<xsl:with-param name="accumulated" select="$accumulated + $n * $m" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$accumulated"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Example of call:
<xsl:call-template name="process">
<xsl:with-param name="number" select="28200703"/>
</xsl:call-template>
Demo: http://xsltransform.net/3MvmrAF

xsl increment counter conditional

I am trying to increment a counter when a condition is met, for example if a final_score is over 30, count = 1. so if 3 scores are over 30 the final_score should be 3. To get the scores i need to add previous scores so the final score is the calculation of all the other scores
for example
Example XML
<scores>
<score result="20" />
<score result="10" />
<score redult="5" />
</score>
Main XSL
<xsl:call-template name="scores">
<xsl:with-param name="prev_count" select="0"/>
</xsl:call-template>
Template for scores
<template name="scores">
<param name="counter" select="0">
<xsl:variable name="prev_counter" select="$counter+1" />
<xsl:for-each select="persons/person[id=#id]">//get all the scores
<xsl:varable name="total_score" select="sum(scores/score)" />//sum them all up
<xsl:if test="total_score > 40" > //check the result
<xsl:value-of select="$counter">
<xsl:call-template name="scores">
<xsl:with-param name="counter" select="$counter+1"/>
</xsl:call-template>
</xsl:if>
<xsl:for-each>
</xsl:template>
</xsl:template>
I just cannot work it out, or is my approach way off?
If I have understood correctly, to count the number of person elements, where the total of the score elements is greater than 30, just use this expression (assuming you are currently positioned on the parent persons element)
<xsl:value-of select="count(person[sum(scores/score/#result) > 30])" />

using Functions vs using templates in XSLT?

What are the pros and cons of functions vs templates in XSLT?
I want to send a unix-timestamp and get an answer like "today" or "tomorrow" or "next week". Which method is most appropriate for this?
The main reason of choosing an <xsl:function> over a named template is the much greater degree of composability of a function.
It is very easy and convenient to write an <xsl:function> that produces the wanted results:
<xsl:function name="my:when" as="xs:string">
<xsl:param name="pDateTime" as="xs:dateTime"/>
<xsl:sequence select=
"for $vToday in xs:dateTime(current-date()),
$vTomorrow in $vToday
+ xs:dayTimeDuration('P1D'),
$vDayAfterTomorrow in $vTomorrow
+ xs:dayTimeDuration('P1D'),
$vNextWeek in $vToday
+ 7* xs:dayTimeDuration('P1D'),
$vNextFortnight in $vNextWeek
+ 7* xs:dayTimeDuration('P1D')
return
if($pDateTime lt $vToday)
then 'in the Past'
else if($pDateTime lt $vTomorrow)
then 'Today'
else if($pDateTime lt $vDayAfterTomorrow)
then 'Tomorrow'
else if($pDateTime lt $vNextWeek)
then 'This week'
else if($pDateTime lt $vNextFortnight)
then 'Next week'
else 'In the Future'
"/>
</xsl:function>
Here is a complete transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:sequence select="my:when(current-dateTime())"/>,
<xsl:sequence select="my:when(current-dateTime()
+xs:dayTimeDuration('P1D'))"/>,
<xsl:sequence select="my:when(current-dateTime()
+xs:dayTimeDuration('P2D'))"/>,
<xsl:sequence select="my:when(current-dateTime()
+xs:dayTimeDuration('P3D'))"/>,
<xsl:sequence select="my:when(current-dateTime()
+xs:dayTimeDuration('P4D'))"/>,
<xsl:sequence select="my:when(current-dateTime()
+xs:dayTimeDuration('P5D'))"/>,
<xsl:sequence select="my:when(current-dateTime()
+xs:dayTimeDuration('P6D'))"/>,
<xsl:sequence select="my:when(current-dateTime()
+xs:dayTimeDuration('P7D'))"/>,
<xsl:sequence select="my:when(current-dateTime()
+xs:dayTimeDuration('P8D'))"/>,
<xsl:sequence select="my:when(current-dateTime()
+xs:dayTimeDuration('P9D'))"/>
</xsl:template>
<xsl:function name="my:when" as="xs:string">
<xsl:param name="pDateTime" as="xs:dateTime"/>
<xsl:sequence select=
"for $vToday in xs:dateTime(current-date()),
$vTomorrow in $vToday
+ xs:dayTimeDuration('P1D'),
$vDayAfterTomorrow in $vTomorrow
+ xs:dayTimeDuration('P1D'),
$vNextWeek in $vToday
+ 7* xs:dayTimeDuration('P1D'),
$vNextFortnight in $vNextWeek
+ 7* xs:dayTimeDuration('P1D')
return
if($pDateTime lt $vToday)
then 'in the Past'
else if($pDateTime lt $vTomorrow)
then 'Today'
else if($pDateTime lt $vDayAfterTomorrow)
then 'Tomorrow'
else if($pDateTime lt $vNextWeek)
then 'This week'
else if($pDateTime lt $vNextFortnight)
then 'Next week'
else 'In the Future'
"/>
</xsl:function>
</xsl:stylesheet>
when this transformation is applied (to any document -- not used), the wanted, correct result is produced:
Today,
Tomorrow,
This week,
This week,
This week,
This week,
This week,
Next week,
Next week,
Next week
In this case, an external function is best-suited.
XSLT is best suited for pattern matching and transformation, not computation.