Umbraco outputting filesize - umbraco

I'm trying to output the correct size for pdf files that has been uploaded.
But the only output is 0 - what am I doing wrong?
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:umbraco.contour="urn:umbraco.contour"
exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets umbraco.contour ">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:param name="currentPage"/>
<xsl:variable name="documentTypeAlias" select="string('PdfItem')"/>
<xsl:variable name="size" select="#currentpage/data [#alias = 'umbracoBytes']" />
<xsl:variable name="sizeAndSuffix">
<xsl:choose>
<xsl:when test="$size >= 1073741824">
<xsl:value-of select="format-number($size div 1073741824,'#,###')"/>
<xsl:text>GB</xsl:text>
</xsl:when>
<xsl:when test="$size >= 1048576">
<xsl:value-of select="format-number($size div 1048576,'#,###')"/>
<xsl:text>MB</xsl:text>
</xsl:when>
<xsl:when test="$size >= 1024">
<xsl:value-of select="format-number($size div 1024,'#,###')"/>
<xsl:text>KB</xsl:text>
</xsl:when>
<xsl:when test="$size > 0 and $size < 1024">
<xsl:value-of select="format-number($size div 0,'#,###')"/>
<xsl:text> Bytes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>0 Bytes</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:template match="/">
<xsl:for-each select="$currentPage/node [#nodeTypeAlias = $documentTypeAlias and string(data [#alias='umbracoNaviHide']) != '0']">
<div class="pdf">
<a>
<xsl:attribute name="class">pdfmenu</xsl:attribute>
<xsl:attribute name="href"><xsl:value-of select="umbraco.library:GetMedia(./data[#alias='pdf'], 0)/data [#alias = 'umbracoFile']"/></xsl:attribute>
<strong><xsl:value-of select="#nodeName"/><span>
(<xsl:value-of select="$sizeAndSuffix"/>)</span></strong>
<em><xsl:value-of select="data [#alias = 'PDFBeskrivelse']"/></em>
<img>
<xsl:attribute name="src"><xsl:value-of select="data [#alias = 'PDFBillede']"/></xsl:attribute>
<xsl:attribute name="alt"></xsl:attribute>
<xsl:attribute name="height">200</xsl:attribute>
<xsl:attribute name="width">141</xsl:attribute>
</img>
</a>
</div>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Alright, I've move the size variable into the for-each loop, as it varies per file and fixed a few typos:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
<!ENTITY nbsp " ">
]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:umbraco.contour="urn:umbraco.contour"
exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets umbraco.contour ">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:param name="currentPage"/>
<xsl:variable name="documentTypeAlias" select="string('PdfItem')"/>
<xsl:template match="/">
<xsl:for-each select="$currentPage/node [#nodeTypeAlias = $documentTypeAlias and string(data [#alias='umbracoNaviHide']) != '0']">
<xsl:if test="string(data [#alias = 'pdf']) != ''">
<xsl:variable name="size" select="umbraco.library:GetMedia(data[#alias='pdf'], 0)/data [#alias = 'umbracoFile']" />
<xsl:variable name="sizeAndSuffix">
<xsl:choose>
<xsl:when test="$size >= 1073741824">
<xsl:value-of select="format-number($size div 1073741824,'#,###')"/>
<xsl:text>GB</xsl:text>
</xsl:when>
<xsl:when test="$size >= 1048576">
<xsl:value-of select="format-number($size div 1048576,'#,###')"/>
<xsl:text>MB</xsl:text>
</xsl:when>
<xsl:when test="$size >= 1024">
<xsl:value-of select="format-number($size div 1024,'#,###')"/>
<xsl:text>KB</xsl:text>
</xsl:when>
<xsl:when test="$size > 0 and $size < 1024">
<xsl:value-of select="format-number($size div 0,'#,###')"/>
<xsl:text> Bytes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>0 Bytes</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<div class="pdf">
<a>
<xsl:attribute name="class">pdfmenu</xsl:attribute>
<xsl:attribute name="href">
<xsl:value-of select="umbraco.library:GetMedia(data[#alias='pdf'], 0)/data [#alias = 'umbracoFile']"/>
</xsl:attribute>
<strong>
<xsl:value-of select="#nodeName"/>
<span>
(<xsl:value-of select="$sizeAndSuffix"/>)
</span>
</strong>
<em>
<xsl:value-of select="data [#alias = 'PDFBeskrivelse']"/>
</em>
<img>
<xsl:attribute name="src">
<xsl:value-of select="data [#alias = 'PDFBillede']"/>
</xsl:attribute>
<xsl:attribute name="alt"></xsl:attribute>
<xsl:attribute name="height">200</xsl:attribute>
<xsl:attribute name="width">141</xsl:attribute>
</img>
</a>
</div>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
That should do the trick.

Related

Need help in grouping and transposing data using XSLT

I need help in conditional grouping and transposing XML data.
I need to group and transpose data using week of the month, Worker ID and Rate_Category_Code. Need to report the Hours_Worked in the appropriate weeks. Hours would be blank for all other week. Data in XML will be never for more then a month.
If there are overtime hours reported (Identified by Rate_Category_Code as OT) then i need to create separate row for the week in which overtime hours was reported.
Below is the XML data -
``
<?xml version="1.0" encoding="UTF-8"?>
<wd:Report_Data xmlns:wd="urn:com.workday.report/INT1120_CR_HR_Fieldglass_Daily_Timesheet">
<wd:Report_Entry>
<wd:Worker_ID>FNRAWK00001743</wd:Worker_ID>
<wd:Task_Code>01</wd:Task_Code>
<wd:GL_Account_Code>Default</wd:GL_Account_Code>
<wd:Rate_Category_Code>ST</wd:Rate_Category_Code>
<wd:UOM>HR</wd:UOM>
<wd:Hours_Worked>8</wd:Hours_Worked>
<wd:Reported_Date>2019-06-10</wd:Reported_Date>
</wd:Report_Entry>
<wd:Report_Entry>
<wd:Worker_ID>FNRAWK00001743</wd:Worker_ID>
<wd:Task_Code>01</wd:Task_Code>
<wd:GL_Account_Code>Default</wd:GL_Account_Code>
<wd:Rate_Category_Code>ST</wd:Rate_Category_Code>
<wd:UOM>HR</wd:UOM>
<wd:Hours_Worked>8</wd:Hours_Worked>
<wd:Reported_Date>2019-06-18</wd:Reported_Date>
</wd:Report_Entry>
<wd:Report_Entry>
<wd:Worker_ID>FNRAWK00001743</wd:Worker_ID>
<wd:Task_Code>01</wd:Task_Code>
<wd:GL_Account_Code>Default</wd:GL_Account_Code>
<wd:Rate_Category_Code>OT</wd:Rate_Category_Code>
<wd:UOM>HR</wd:UOM>
<wd:Hours_Worked>1</wd:Hours_Worked>
<wd:Reported_Date>2019-06-10</wd:Reported_Date>
</wd:Report_Entry>
<wd:Report_Entry>
<wd:Worker_ID>FNRAWK00001742</wd:Worker_ID>
<wd:Task_Code>01</wd:Task_Code>
<wd:GL_Account_Code>Default</wd:GL_Account_Code>
<wd:Rate_Category_Code>ST</wd:Rate_Category_Code>
<wd:UOM>HR</wd:UOM>
<wd:Hours_Worked>9</wd:Hours_Worked>
<wd:Reported_Date>2019-06-20</wd:Reported_Date>
</wd:Report_Entry>
</wd:Report_Data>
``
Desired output -
``
Type=Upload Full Time Sheet with Revision
Transaction=True
Approval Required=True
Messaging Required=False
Language=English (United States)
Number Format=#,##9.99 (Example: 1,234,567.99)
Date Format=MM/DD/YYYY
Submit=FALSE
Buyer=FNRA
Supplier Review=False
Comments=
Worker_ID|Month_Start_Date|First_Monday|Cost_Center_Code|Task_Code|GL_Account_Code|Rate_Category_Code|UOM|Mon_Hrs|Tue_Hrs|Wed_Hrs|Thu_Hrs|Fri_Hrs|Sat_Hrs|Sun_Hrs
FNRAWK00001743|06/01/2019|05/27/2019||01|Default|ST|HR||||||||
FNRAWK00001743|06/01/2019|06/03/2019||01|Default|ST|HR||||||||
FNRAWK00001743|06/01/2019|06/10/2019||01|Default|ST|HR|8|||||||
FNRAWK00001743|06/01/2019|06/10/2019||01|Default|OT|HR|1|||||||
FNRAWK00001743|06/01/2019|06/17/2019||01|Default|ST|HR||8||||||
FNRAWK00001743|06/01/2019|06/24/2019||01|Default|ST|HR||||||||
FNRAWK00001742|06/01/2019|05/27/2019||01|Default|ST|HR||||||||
FNRAWK00001742|06/01/2019|06/03/2019||01|Default|ST|HR||||||||
FNRAWK00001742|06/01/2019|06/10/2019||01|Default|ST|HR||||||||
FNRAWK00001742|06/01/2019|06/10/2019||01|Default|OT|HR||||||||
FNRAWK00001742|06/01/2019|06/17/2019||01|Default|ST|HR||||9||||
FNRAWK00001742|06/01/2019|06/24/2019||01|Default|ST|HR||||||||
``
XSLT -
``
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wd="urn:com.workday.report/INT1120_CR_HR_Fieldglass_Daily_Timesheet" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:my="http://www.example.com/my" xmlns:functx="http://www.functx.com" exclude-result-prefixes="xs my functx">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
<xsl:variable name="Linefeed" select="'
'"/>
<xsl:variable name="Delimiter" select=" '|' "/>
<xsl:function name="functx:repeat-string" as="xs:string">
<xsl:param name="stringToRepeat" as="xs:string?"/>
<xsl:param name="count" as="xs:integer"/>
<xsl:sequence select="string-join((for $i in 1 to $count return $stringToRepeat),'')"/>
</xsl:function>
<xsl:function name="functx:pad-integer-to-length" as="xs:string">
<xsl:param name="integerToPad" as="xs:anyAtomicType?"/>
<xsl:param name="length" as="xs:integer"/>
<xsl:sequence select="if ($length < string-length(string($integerToPad))) then error(xs:QName('functx:Integer_Longer_Than_Length')) else concat (functx:repeat-string('0',$length - string-length(string($integerToPad))),string($integerToPad))"/>
</xsl:function>
<xsl:function name="functx:date" as="xs:date">
<xsl:param name="year" as="xs:anyAtomicType"/>
<xsl:param name="month" as="xs:anyAtomicType"/>
<xsl:param name="day" as="xs:anyAtomicType"/>
<xsl:sequence select="xs:date(concat(functx:pad-integer-to-length(xs:integer($year),4),'-',functx:pad-integer-to-length(xs:integer($month),2),'-',functx:pad-integer-to-length(xs:integer($day),2)))"/>
</xsl:function>
<!--Function which accepts date as input and returns the date for first day of the month (Input Date)-->
<xsl:function name="functx:first-day-of-month" as="xs:date?">
<xsl:param name="date" as="xs:anyAtomicType?"/>
<xsl:sequence select="functx:date(year-from-date(xs:date($date)),month-from-date(xs:date($date)),1)"/>
</xsl:function>
<!--Function which accepts date as input and returns the date for first day of the week-->
<xsl:function name="my:thisMonday">
<xsl:param name="date"/>
<xsl:variable name="epoch" select="xs:date('0001-01-01')"/>
<xsl:variable name="dayNumber" select="fn:days-from-duration($date - $epoch)"/>
<xsl:variable name="dayOfWeek" select="$dayNumber mod 7"/>
<xsl:value-of select="$date - xs:dayTimeDuration(concat('P', $dayOfWeek, 'D' ))"/>
</xsl:function>
<xsl:template match="wd:Report_Data">
<File>
<Header>
<xsl:text>Type=Upload Full Time Sheet with Revision</xsl:text>
<xsl:value-of select="$Linefeed"/>
<xsl:text>Transaction=True</xsl:text>
<xsl:value-of select="$Linefeed"/>
<xsl:text>Approval Required=True</xsl:text>
<xsl:value-of select="$Linefeed"/>
<xsl:text>Messaging Required=False</xsl:text>
<xsl:value-of select="$Linefeed"/>
<xsl:text>Language=English (United States)</xsl:text>
<xsl:value-of select="$Linefeed"/>
<xsl:text>Number Format=#,##9.99 (Example: 1,234,567.99)</xsl:text>
<xsl:value-of select="$Linefeed"/>
<xsl:text>Date Format=MM/DD/YYYY</xsl:text>
<xsl:value-of select="$Linefeed"/>
<xsl:text>Submit=FALSE</xsl:text>
<xsl:value-of select="$Linefeed"/>
<xsl:text>Buyer=FNRA</xsl:text>
<xsl:value-of select="$Linefeed"/>
<xsl:text>Supplier Review=False</xsl:text>
<xsl:value-of select="$Linefeed"/>
<xsl:text>Comments=</xsl:text>
<xsl:value-of select="$Linefeed"/>
</Header>
<xsl:value-of select="$Linefeed"/>
<Header>
<xsl:text>Worker_ID|Date|Week_Start_Date|Cost_Center_Code|Task_Code|GL_Account_Code|Rate_Category_Code|UOM|Mon_Hrs|Tue_Hrs|Wed_Hrs|Thu_Hrs|Fri_Hrs|Sat_Hrs|Sun_Hrs</xsl:text>
<xsl:value-of select="$Linefeed"/>
</Header>
<xsl:for-each select="wd:Report_Entry">
<xsl:call-template name="Write_Rows"/>
</xsl:for-each>
</File>
</xsl:template>
<xsl:template name="Write_Rows">
<Record>
<Worker_ID>
<xsl:value-of select="wd:Worker_ID"/>
</Worker_ID>
<xsl:value-of select="$Delimiter"/>
<Date>
<!--<xsl:value-of select="concat(substring(wd:Date,6, 2),'/',substring(wd:Date, 9, 2),'/',substring(wd:Date, 1, 4))"/>-->
<!--Pass date in YYYY-MM-DD format to custom function and format the return value to 'MM/DD/YYYY'-->
<xsl:variable name="StartDateOfMonth" select="string(functx:first-day-of-month(xs:date(concat(substring(wd:Reported_Date, 1, 4),'-', substring(wd:Reported_Date,6, 2),'-',substring(wd:Reported_Date, 9, 2)))))"/>
<xsl:value-of select="concat(substring($StartDateOfMonth,6,2),'/',substring($StartDateOfMonth,9,2),'/',substring($StartDateOfMonth,1,4))"/>
</Date>
<xsl:value-of select="$Delimiter"/>
<Week_Start_Date>
<!--<xsl:variable name="WeekStartDate" select="my:thisMonday(xs:date('2019-06-01'))"/>-->
<!--Format the return value to 'MM/DD/YYYY'-->
<xsl:variable name="WeekStartDate" select="my:thisMonday(xs:date(wd:Reported_Date))"/>
<xsl:value-of select="concat(substring($WeekStartDate,6,2),'/',substring($WeekStartDate,9,2),'/',substring($WeekStartDate,1,4))"/>
</Week_Start_Date>
<xsl:value-of select="$Delimiter"/>
<Cost_Center_Code>
<xsl:value-of select="wd:Cost_Center_Code"/>
</Cost_Center_Code>
<xsl:value-of select="$Delimiter"/>
<Task_Code>
<xsl:value-of select="wd:Task_Code"/>
</Task_Code>
<xsl:value-of select="$Delimiter"/>
<GL_Account_Code>
<xsl:value-of select="wd:GL_Account_Code"/>
</GL_Account_Code>
<xsl:value-of select="$Delimiter"/>
<Rate_Category_Code>
<xsl:value-of select="wd:Rate_Category_Code"/>
</Rate_Category_Code>
<xsl:value-of select="$Delimiter"/>
<UOM>
<xsl:value-of select="wd:UOM"/>
</UOM>
<xsl:value-of select="$Delimiter"/>
<Hours_Worked>
<xsl:value-of select="wd:Hours_Worked"/>
</Hours_Worked>
<xsl:value-of select="$Delimiter"/>
<Overtime>
<xsl:value-of select="wd:Overtime"/>
</Overtime>
<xsl:value-of select="$Linefeed"/>
</Record>
</xsl:template>
</xsl:stylesheet>
``
I think part of the task is grouping with a composite key e.g. in XSLT 3:
<xsl:for-each-group select="wd:Report_Entry" composite="yes" group-by="wd:Worker_ID, wd:Rate_Category_Code, my:thisMonday(xs:date(wd:Reported_Date))">
<xsl:value-of select="current-grouping-key(), sum(current-group()/wd:Hours_Worked)" separator="|"/>
<xsl:text>
</xsl:text>
</xsl:for-each-group>
That gives the existing data:
FNRAWK00001743|ST|2019-06-10|8
FNRAWK00001743|ST|2019-06-17|8
FNRAWK00001743|OT|2019-06-10|1
FNRAWK00001742|ST|2019-06-17|9
You will need to add logic for outputting empty lines for weeks for which there is no data.

Subtraction by decimal number

I have a to subtract the amount -1 based on the condition.Please any one help.
Input:
<JD>
<GP xmlns="">
I xmlns="">
<PK>40</PK>
<A/>
<AMNT>11659650.15</AMNT>
<B/>
<C/>
</I>
<I xmlns="">
<PK>50</PK>
<A/>
<AMNT>11659650.15</AMNT>
<B/>
<C/>
</I>
</GP>
</JD>
Tried with below XSLT and got 1.165964915E7 for 50.
<xsl:for-each select="JD/mo:GP/I">
<xsl:if test="PK='40'">
<xsl:variable name="a" select="AMNT"/>
<xsl:element name="AMT">
<xsl:value-of select="$a"/>
</xsl:element>
</xsl:if>
<xsl:if test="PK='50'">
<xsl:variable name="a" select="AMNT"/>
<xsl:element name="AMT">
<xsl:value-of select="$a - 1"/>
</xsl:element>
</xsl:if>
Considering your given input as following:
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<JD>
<GP>
<I>
<PK>40</PK>
<A />
<AMNT>11659650.15</AMNT>
<B />
<C />
</I>
<I>
<PK>50</PK>
<A />
<AMNT>11659650.15</AMNT>
<B />
<C />
</I>
</GP>
</JD>
</Root>
In XSLT 2.0, you can try it using xs:decimal as below:
<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 omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/Root">
<xsl:for-each select="JD/mo:GP/I">
<xsl:if test="PK='40'">
<xsl:variable name="a" select="AMNT" />
<xsl:element name="AMT">
<xsl:value-of select="$a" />
</xsl:element>
</xsl:if>
<xsl:if test="PK='50'">
<xsl:variable name="a" select="AMNT" />
<xsl:element name="AMT">
<xsl:value-of select="xs:decimal($a) - 1" />
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:template>
In XSLT 1.0, Use the format-number() function:
<xsl:template match="/Root">
<xsl:for-each select="JD/mo:GP/I">
<xsl:if test="PK='40'">
<xsl:variable name="a" select="AMNT" />
<xsl:element name="AMT">
<xsl:value-of select="$a" />
</xsl:element>
</xsl:if>
<xsl:if test="PK='50'">
<xsl:variable name="a" select="AMNT" />
<xsl:element name="AMT">
<xsl:value-of select="format-number($a - 1, '0.##')" />
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:template>

Roman Numeral to integer value using xslt

Is there any function to convert roman number to integer like format-number function can change integer number to roman number. As i am working on conversion a roman number to integer. if there is no function than i have to handle this in templates.
I have handle this through writing own function, but there is an issue that it is not validate the input value such as (VII or IVII is same) but output is correct.
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:number="http://dummy" version="2.0" >
<xsl:output method="text"/>
<xsl:template match="/">
(<xsl:value-of select="number:RomanToInteger('MMXVII', 0)"/>)
</xsl:template>
<xsl:function name="number:RomanToInteger">
<xsl:param name="romannumber"/>
<xsl:param name="followingvalue"/>
<xsl:choose>
<xsl:when test="ends-with($romannumber,'CM')">
<xsl:value-of select="900 + number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-2), 900)"/>
</xsl:when>
<xsl:when test="ends-with($romannumber,'M')">
<xsl:value-of select="1000+ number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-1), 1000)"/>
</xsl:when>
<xsl:when test="ends-with($romannumber,'CD')">
<xsl:value-of select="400+ number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-2), 400)"/>
</xsl:when>
<xsl:when test="ends-with($romannumber,'D')">
<xsl:value-of select="500+ number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-1), 500)"/>
</xsl:when>
<xsl:when test="ends-with($romannumber,'XC')">
<xsl:value-of select="90+ number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-2), 90)"/>
</xsl:when>
<xsl:when test="ends-with($romannumber,'C')">
<xsl:value-of select="(if(100 ge number($followingvalue)) then 100 else -100)+ number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-1), 100)"/>
</xsl:when>
<xsl:when test="ends-with($romannumber,'XL')">
<xsl:value-of select="40+ number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-2), 40)"/>
</xsl:when>
<xsl:when test="ends-with($romannumber,'L')">
<xsl:value-of select="50+ number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-1), 50)"/>
</xsl:when>
<xsl:when test="ends-with($romannumber,'IX')">
<xsl:value-of select="9+ number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-2), 9)"/>
</xsl:when>
<xsl:when test="ends-with($romannumber,'X')">
<xsl:value-of select="(if(10 ge number($followingvalue)) then 10 else -10) + number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-1), 10)"/>
</xsl:when>
<xsl:when test="ends-with($romannumber,'IV')">
<xsl:value-of select="4+ number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-2), 4)"/>
</xsl:when>
<xsl:when test="ends-with($romannumber,'V')">
<xsl:value-of select="5+ number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-1), 5)"/>
</xsl:when>
<xsl:when test="ends-with($romannumber,'I')">
<xsl:value-of select="(if(1 ge number($followingvalue)) then 1 else -1)+ number:RomanToInteger(substring($romannumber,1,string-length($romannumber)-1), 1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="0"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</xsl:stylesheet>

Limiting output (XSLT 2.0 Temporary Tree)

A co-worker and I have been given a new requirement. Please review the following.
We have a requirement to transform an XML source document while enforcing a numerical limit on certain items in the result document. A source document, an XSLT stylesheet, and a result document are reproduced below. This result document, however, does not reflect the desired numerical limit, because this stylesheet transforms the source document without any restriction.
At the end, our question will be: How can the desired numerical limit be enforced in the stylesheet?
Source document:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<T:Message xmlns="http://www.noname.com/Platform" xmlns:T="http://www.noname.com/Transportation">
<T:ShipmentListMessage>
<Shipment>
<ShipmentNumber>ABC</ShipmentNumber>
<PickupCommodityCode>Hazmat</PickupCommodityCode>
<DeliveryCommodityCode>Hazmat</DeliveryCommodityCode>
<ShipmentLine>
<ShipmentLineNumber>ABC-1</ShipmentLineNumber>
<OrderLineItemNumber>1</OrderLineItemNumber>
</ShipmentLine>
<ShipmentLine>
<ShipmentLineNumber>ABC-2</ShipmentLineNumber>
<OrderLineItemNumber>2</OrderLineItemNumber>
</ShipmentLine>
<ShipmentLine>
<ShipmentLineNumber>ABC-3</ShipmentLineNumber>
<OrderLineItemNumber>3</OrderLineItemNumber>
</ShipmentLine>
</Shipment>
<Shipment>
<ShipmentNumber>DEF</ShipmentNumber>
<PickupCommodityCode>Hazmat</PickupCommodityCode>
<DeliveryCommodityCode>Hazmat</DeliveryCommodityCode>
<ShipmentLine>
<ShipmentLineNumber>DEF-1</ShipmentLineNumber>
<OrderLineItemNumber>1</OrderLineItemNumber>
</ShipmentLine>
<ShipmentLine>
<ShipmentLineNumber>DEF-2</ShipmentLineNumber>
<OrderLineItemNumber>2</OrderLineItemNumber>
</ShipmentLine>
</Shipment>
<Shipment>
<ShipmentNumber>GHI</ShipmentNumber>
<PickupCommodityCode>Hazmat</PickupCommodityCode>
<DeliveryCommodityCode>Hazmat</DeliveryCommodityCode>
<ShipmentLine>
<ShipmentLineNumber>GHI-1</ShipmentLineNumber>
<OrderLineItemNumber>1</OrderLineItemNumber>
</ShipmentLine>
<ShipmentLine>
<ShipmentLineNumber>GHI-2</ShipmentLineNumber>
<OrderLineItemNumber>2</OrderLineItemNumber>
</ShipmentLine>
<ShipmentLine>
<ShipmentLineNumber>GHI-3</ShipmentLineNumber>
<OrderLineItemNumber>3</OrderLineItemNumber>
</ShipmentLine>
<ShipmentLine>
<ShipmentLineNumber>GHI-4</ShipmentLineNumber>
<OrderLineItemNumber>4</OrderLineItemNumber>
</ShipmentLine>
</Shipment>
</T:ShipmentListMessage>
<T:MovementListMessage>
<Movement>
<MovementStop>
<StopSequenceNumber>0</StopSequenceNumber>
<MovementStopLine>
<OperationType>Pickup</OperationType>
<ShipmentNumber>ABC</ShipmentNumber>
</MovementStopLine>
<MovementStopLine>
<OperationType>Pickup</OperationType>
<ShipmentNumber>DEF</ShipmentNumber>
</MovementStopLine>
<MovementStopLine>
<OperationType>Pickup</OperationType>
<ShipmentNumber>GHI</ShipmentNumber>
</MovementStopLine>
</MovementStop>
<MovementStop>
<StopSequenceNumber>1</StopSequenceNumber>
<MovementStopLine>
<OperationType>Deliver</OperationType>
<ShipmentNumber>ABC</ShipmentNumber>
</MovementStopLine>
</MovementStop>
<MovementStop>
<StopSequenceNumber>2</StopSequenceNumber>
<MovementStopLine>
<OperationType>Deliver</OperationType>
<ShipmentNumber>DEF</ShipmentNumber>
</MovementStopLine>
<MovementStopLine>
<OperationType>Deliver</OperationType>
<ShipmentNumber>GHI</ShipmentNumber>
</MovementStopLine>
</MovementStop>
</Movement>
</T:MovementListMessage>
</T:Message>
XSLT Stylesheet:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:t="http://www.noname.com/Transportation" xmlns:vc="http://www.noname.com/Platform">
<xsl:output method="text" indent="no"/>
<xsl:template match="t:Message">
<xsl:variable name="shipmentPath" select="t:ShipmentListMessage/vc:Shipment"/>
<xsl:variable name="movementPath" select="t:MovementListMessage/vc:Movement"/>
<xsl:variable name="numDeliverLocations" select="count($movementPath/vc:MovementStop/vc:MovementStopLine[vc:OperationType = 'Deliver'])"/>
<xsl:variable name="numOffers" select="count($shipmentPath)"/>
<xsl:for-each select="$movementPath/vc:MovementStop">
<xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Deliver']">
<xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
<xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Deliver'])"/>
<xsl:if test="position() = 1">
S5*<xsl:value-of select="../vc:StopSequenceNumber+1"/>*UL<xsl:text>~</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Deliver']">
<xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
<xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Deliver'])"/>
<xsl:variable name="OfferPosition" select="if (position() lt 10) then position() else 0"/>
<xsl:for-each select="$shipmentPath[vc:ShipmentNumber = $OfferNumber and vc:DeliveryCommodityCode != '']">
<xsl:choose>
<xsl:when test="vc:DeliveryCommodityCode = 'Hazmat'">
<xsl:if test="$numOffers gt 1 or $numDeliverLocations = 1">
<xsl:for-each select="vc:ShipmentLine">
<xsl:variable name="ShipmentLine" select="vc:ShipmentLineNumber"/>
<xsl:choose>
<xsl:when test="$OfferCount eq 1">
L5*<xsl:value-of select="vc:OrderLineItemNumber"/><xsl:text>~</xsl:text>
</xsl:when>
<xsl:when test="position() lt 10">
L5*<xsl:value-of select="concat($OfferPosition, '0', vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text>
</xsl:when>
<xsl:otherwise>
L5*<xsl:value-of select="concat($OfferPosition, vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:if>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
<xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Pickup']">
<xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
<xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Pickup'])"/>
<xsl:if test="position() = 1">
S5*<xsl:value-of select="../vc:StopSequenceNumber+1"/>*LD<xsl:text>~</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Pickup']">
<xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
<xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Pickup'])"/>
<xsl:variable name="OfferPosition" select="if (position() lt 10) then position() else 0"/>
<xsl:for-each select="$shipmentPath[vc:ShipmentNumber = $OfferNumber and vc:PickupCommodityCode != '']">
<xsl:choose>
<xsl:when test="vc:PickupCommodityCode = 'Hazmat'">
<xsl:for-each select="vc:ShipmentLine">
<xsl:variable name="ShipmentLine" select="vc:ShipmentLineNumber"/>
<xsl:choose>
<xsl:when test="$OfferCount eq 1">
L5*<xsl:value-of select="vc:OrderLineItemNumber"/><xsl:text>~</xsl:text>
</xsl:when>
<xsl:when test="position() lt 10">
L5*<xsl:value-of select="concat($OfferPosition, '0', vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text>
</xsl:when>
<xsl:otherwise>
L5*<xsl:value-of select="concat($OfferPosition, vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Result document:
S5*1*LD~
L5*101~
L5*102~
L5*103~
L5*201~
L5*202~
L5*301~
L5*302~
L5*303~
L5*304~
S5*2*UL~
L5*1~
L5*2~
L5*3~
S5*3*UL~
L5*101~
L5*102~
L5*201~
L5*202~
L5*203~
L5*204~
In order to satisfy our requirement, the numerical limit must somehow be built into the stylesheet. For the purpose of this discussion, the desired limit is 5 on the number of “L5” segments following an “S5” segment. In this example, the limit will reduce the number of “L5” segments to 5 each following “S5*1*LD” and “S5*3*UL”. The limit will not come into play in the case of “S5*2*UL”.
Desired result document with the “L5” limit applied:
S5*1*LD~
L5*101~
L5*102~
L5*103~
L5*201~
L5*202~
S5*2*UL~
L5*1~
L5*2~
L5*3~
S5*3*UL~
L5*101~
L5*102~
L5*201~
L5*202~
L5*203~
Again, our question is: How can the desired numerical limit be coded in the stylesheet?
I think you could store the first result in a variable and then group and count as needed, for easier processing I have chosen to wrap the intermediary result as XML:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:t="http://www.noname.com/Transportation" xmlns:vc="http://www.noname.com/Platform"
exclude-result-prefixes="t vc">
<xsl:output method="text"/>
<xsl:template match="t:Message">
<xsl:variable name="shipmentPath" select="t:ShipmentListMessage/vc:Shipment"/>
<xsl:variable name="movementPath" select="t:MovementListMessage/vc:Movement"/>
<xsl:variable name="numDeliverLocations" select="count($movementPath/vc:MovementStop/vc:MovementStopLine[vc:OperationType = 'Deliver'])"/>
<xsl:variable name="numOffers" select="count($shipmentPath)"/>
<xsl:variable name="items">
<xsl:for-each select="$movementPath/vc:MovementStop">
<xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Deliver']">
<xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
<xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Deliver'])"/>
<xsl:if test="position() = 1">
<item>S5*<xsl:value-of select="../vc:StopSequenceNumber+1"/>*UL<xsl:text>~</xsl:text></item>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Deliver']">
<xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
<xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Deliver'])"/>
<xsl:variable name="OfferPosition" select="if (position() lt 10) then position() else 0"/>
<xsl:for-each select="$shipmentPath[vc:ShipmentNumber = $OfferNumber and vc:DeliveryCommodityCode != '']">
<xsl:choose>
<xsl:when test="vc:DeliveryCommodityCode = 'Hazmat'">
<xsl:if test="$numOffers gt 1 or $numDeliverLocations = 1">
<xsl:for-each select="vc:ShipmentLine">
<xsl:variable name="ShipmentLine" select="vc:ShipmentLineNumber"/>
<xsl:choose>
<xsl:when test="$OfferCount eq 1">
<item>L5*<xsl:value-of select="vc:OrderLineItemNumber"/><xsl:text>~</xsl:text></item>
</xsl:when>
<xsl:when test="position() lt 10">
<item>L5*<xsl:value-of select="concat($OfferPosition, '0', vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text></item>
</xsl:when>
<xsl:otherwise>
<item>L5*<xsl:value-of select="concat($OfferPosition, vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text></item>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:if>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
<xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Pickup']">
<xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
<xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Pickup'])"/>
<xsl:if test="position() = 1">
<item>S5*<xsl:value-of select="../vc:StopSequenceNumber+1"/>*LD<xsl:text>~</xsl:text></item>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="vc:MovementStopLine[vc:OperationType = 'Pickup']">
<xsl:variable name="OfferNumber" select="vc:ShipmentNumber"/>
<xsl:variable name="OfferCount" select="count(../vc:MovementStopLine[vc:OperationType = 'Pickup'])"/>
<xsl:variable name="OfferPosition" select="if (position() lt 10) then position() else 0"/>
<xsl:for-each select="$shipmentPath[vc:ShipmentNumber = $OfferNumber and vc:PickupCommodityCode != '']">
<xsl:choose>
<xsl:when test="vc:PickupCommodityCode = 'Hazmat'">
<xsl:for-each select="vc:ShipmentLine">
<xsl:variable name="ShipmentLine" select="vc:ShipmentLineNumber"/>
<xsl:choose>
<xsl:when test="$OfferCount eq 1">
<item>L5*<xsl:value-of select="vc:OrderLineItemNumber"/><xsl:text>~</xsl:text></item>
</xsl:when>
<xsl:when test="position() lt 10">
<item>L5*<xsl:value-of select="concat($OfferPosition, '0', vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text></item>
</xsl:when>
<xsl:otherwise>
<item>L5*<xsl:value-of select="concat($OfferPosition, vc:OrderLineItemNumber)"/><xsl:text>~</xsl:text></item>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<xsl:for-each-group select="$items/item" group-starting-with="item[starts-with(., 'S5*')]">
<xsl:if test="position() gt 1"><xsl:text>
</xsl:text></xsl:if>
<xsl:value-of select="if (starts-with(., 'S5*1*LD') or starts-with(., 'S5*3*UL'))
then current-group()[position() le 6] else current-group()"
separator="
"/>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
With that stylesheet and your input Saxon 9.5 outputs
S5*1*LD~
L5*101~
L5*102~
L5*103~
L5*201~
L5*202~
S5*2*UL~
L5*1~
L5*2~
L5*3~
S5*3*UL~
L5*101~
L5*102~
L5*201~
L5*202~
L5*203~

XSLT Wrapping text with multiple tags

I have this span element with a class of autbib-pc-bold-italic
<span class="autbib-pc-bold-italic">autbib</span>
I want to create element tags base on the #class attribute value:
My output should be:
<autbib><pc><bold><italic>autbib</italic></bold></pc></autbib>
Here is my xsl templates:
<xsl:template match="span[contains(#class,'autbib')]">
<xsl:call-template name="pbib.loop">
<xsl:with-param name="count" select="count(tokenize(#class, '-'))"/>
<xsl:with-param name="class" select="tokenize(#class, '-')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="pbib.loop">
<xsl:param name="index" select="1" />
<xsl:param name="count" select="count(tokenize(#class, '-')) + 1"/>
<xsl:param name="class" select="tokenize(#class, '-')"/>
<xsl:element name="{$class[1]}">
<xsl:if test="not($index = $count)">
<xsl:element name="{$class[$index]}">
<xsl:apply-templates/>
</xsl:element>
</xsl:if>
</xsl:element>
<xsl:if test="not($index = $count)">
<xsl:call-template name="pbib.loop">
<xsl:with-param name="index" select="$index + 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
And have this output which is wrong:
<autbib>
<pc>autbib</pc>
<bold>autbib</bold>
<italic>autbib</italic>
</autbib>
I need to have this output:
<autbib>
<pc>
<bold>
<italic>autbib</italic>
</bold>
</pc>
</autbib>
My problem is that I'm not sure where I should place xsl:apply-template so that tags wrap with each other.
Here is my suggestion:
<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 indent="yes"/>
<xsl:template match="span[contains(#class,'autbib')]">
<xsl:param name="classes" select="tokenize(#class, '-')"/>
<xsl:choose>
<xsl:when test="not($classes[1])">
<xsl:apply-templates/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{$classes[1]}">
<xsl:apply-templates select=".">
<xsl:with-param name="classes" select="$classes[position() gt 1]"/>
</xsl:apply-templates>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
With Saxon 9.4 that transforms the input
<span class="autbib-pc-bold-italic">autbib</span>
into the result
<autbib>
<pc>
<bold>
<italic>autbib</italic>
</bold>
</pc>
</autbib>

Resources